mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-06-18 18:40:47 +02:00
Merge remote-tracking branch 'upstream/master' into feature/quest
This commit is contained in:
commit
a5f84ab51a
59 changed files with 751 additions and 529 deletions
|
@ -14,7 +14,7 @@ link_hifi_libraries(
|
||||||
audio avatars octree gpu graphics shaders fbx hfm entities
|
audio avatars octree gpu graphics shaders fbx hfm entities
|
||||||
networking animation recording shared script-engine embedded-webserver
|
networking animation recording shared script-engine embedded-webserver
|
||||||
controllers physics plugins midi image
|
controllers physics plugins midi image
|
||||||
model-networking ktx shaders
|
material-networking model-networking ktx shaders
|
||||||
)
|
)
|
||||||
|
|
||||||
add_dependencies(${TARGET_NAME} oven)
|
add_dependencies(${TARGET_NAME} oven)
|
||||||
|
|
|
@ -324,12 +324,6 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode*
|
||||||
if (isFullScene) {
|
if (isFullScene) {
|
||||||
// we're forcing a full scene, clear the force in OctreeQueryNode so we don't force it next time again
|
// we're forcing a full scene, clear the force in OctreeQueryNode so we don't force it next time again
|
||||||
nodeData->setShouldForceFullScene(false);
|
nodeData->setShouldForceFullScene(false);
|
||||||
} else {
|
|
||||||
// we aren't forcing a full scene, check if something else suggests we should
|
|
||||||
isFullScene = nodeData->haveJSONParametersChanged() ||
|
|
||||||
(nodeData->hasConicalViews() &&
|
|
||||||
(nodeData->getViewFrustumJustStoppedChanging() ||
|
|
||||||
nodeData->hasLodChanged()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nodeData->isPacketWaiting()) {
|
if (nodeData->isPacketWaiting()) {
|
||||||
|
|
|
@ -205,8 +205,8 @@ endif()
|
||||||
# link required hifi libraries
|
# link required hifi libraries
|
||||||
link_hifi_libraries(
|
link_hifi_libraries(
|
||||||
shared workload task octree ktx gpu gl procedural graphics graphics-scripting render
|
shared workload task octree ktx gpu gl procedural graphics graphics-scripting render
|
||||||
pointers
|
pointers recording hfm fbx networking material-networking
|
||||||
recording hfm fbx networking model-networking model-baker entities avatars trackers
|
model-networking model-baker entities avatars trackers
|
||||||
audio audio-client animation script-engine physics
|
audio audio-client animation script-engine physics
|
||||||
render-utils entities-renderer avatars-renderer ui qml auto-updater midi
|
render-utils entities-renderer avatars-renderer ui qml auto-updater midi
|
||||||
controllers plugins image trackers
|
controllers plugins image trackers
|
||||||
|
|
|
@ -103,7 +103,7 @@
|
||||||
#include <MessagesClient.h>
|
#include <MessagesClient.h>
|
||||||
#include <hfm/ModelFormatRegistry.h>
|
#include <hfm/ModelFormatRegistry.h>
|
||||||
#include <model-networking/ModelCacheScriptingInterface.h>
|
#include <model-networking/ModelCacheScriptingInterface.h>
|
||||||
#include <model-networking/TextureCacheScriptingInterface.h>
|
#include <material-networking/TextureCacheScriptingInterface.h>
|
||||||
#include <ModelEntityItem.h>
|
#include <ModelEntityItem.h>
|
||||||
#include <NetworkAccessManager.h>
|
#include <NetworkAccessManager.h>
|
||||||
#include <NetworkingConstants.h>
|
#include <NetworkingConstants.h>
|
||||||
|
@ -155,7 +155,7 @@
|
||||||
#include <RenderableEntityItem.h>
|
#include <RenderableEntityItem.h>
|
||||||
#include <RenderableTextEntityItem.h>
|
#include <RenderableTextEntityItem.h>
|
||||||
#include <RenderableWebEntityItem.h>
|
#include <RenderableWebEntityItem.h>
|
||||||
#include <model-networking/MaterialCache.h>
|
#include <material-networking/MaterialCache.h>
|
||||||
#include "recording/ClipCache.h"
|
#include "recording/ClipCache.h"
|
||||||
|
|
||||||
#include "AudioClient.h"
|
#include "AudioClient.h"
|
||||||
|
|
|
@ -41,7 +41,7 @@ QSharedPointer<Resource> AnimationCache::createResource(const QUrl& url) {
|
||||||
}
|
}
|
||||||
|
|
||||||
QSharedPointer<Resource> AnimationCache::createResourceCopy(const QSharedPointer<Resource>& resource) {
|
QSharedPointer<Resource> AnimationCache::createResourceCopy(const QSharedPointer<Resource>& resource) {
|
||||||
return QSharedPointer<Resource>(new Animation(*resource.staticCast<Animation>().data()), &Resource::deleter);
|
return QSharedPointer<Resource>(new Animation(*resource.staticCast<Animation>()), &Resource::deleter);
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimationReader::AnimationReader(const QUrl& url, const QByteArray& data) :
|
AnimationReader::AnimationReader(const QUrl& url, const QByteArray& data) :
|
||||||
|
|
|
@ -40,5 +40,5 @@ QSharedPointer<Resource> SoundCache::createResource(const QUrl& url) {
|
||||||
}
|
}
|
||||||
|
|
||||||
QSharedPointer<Resource> SoundCache::createResourceCopy(const QSharedPointer<Resource>& resource) {
|
QSharedPointer<Resource> SoundCache::createResourceCopy(const QSharedPointer<Resource>& resource) {
|
||||||
return QSharedPointer<Resource>(new Sound(*resource.staticCast<Sound>().data()), &Resource::deleter);
|
return QSharedPointer<Resource>(new Sound(*resource.staticCast<Sound>()), &Resource::deleter);
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
set(TARGET_NAME avatars-renderer)
|
set(TARGET_NAME avatars-renderer)
|
||||||
setup_hifi_library(Network Script)
|
setup_hifi_library(Network Script)
|
||||||
link_hifi_libraries(shared shaders gpu graphics animation model-networking script-engine render render-utils image trackers entities-renderer)
|
link_hifi_libraries(shared shaders gpu graphics animation material-networking model-networking script-engine render render-utils image trackers entities-renderer)
|
||||||
include_hifi_library_headers(avatars)
|
include_hifi_library_headers(avatars)
|
||||||
include_hifi_library_headers(networking)
|
include_hifi_library_headers(networking)
|
||||||
include_hifi_library_headers(hfm)
|
include_hifi_library_headers(hfm)
|
||||||
|
|
|
@ -2,6 +2,7 @@ set(TARGET_NAME display-plugins)
|
||||||
setup_hifi_library(Gui)
|
setup_hifi_library(Gui)
|
||||||
link_hifi_libraries(shared shaders plugins ui-plugins gl ui render-utils ${PLATFORM_GL_BACKEND})
|
link_hifi_libraries(shared shaders plugins ui-plugins gl ui render-utils ${PLATFORM_GL_BACKEND})
|
||||||
include_hifi_library_headers(gpu)
|
include_hifi_library_headers(gpu)
|
||||||
|
include_hifi_library_headers(material-networking)
|
||||||
include_hifi_library_headers(model-networking)
|
include_hifi_library_headers(model-networking)
|
||||||
include_hifi_library_headers(networking)
|
include_hifi_library_headers(networking)
|
||||||
include_hifi_library_headers(graphics)
|
include_hifi_library_headers(graphics)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
set(TARGET_NAME entities-renderer)
|
set(TARGET_NAME entities-renderer)
|
||||||
setup_hifi_library(Network Script)
|
setup_hifi_library(Network Script)
|
||||||
link_hifi_libraries(shared workload gpu shaders procedural graphics model-networking script-engine render render-utils image qml ui pointers)
|
link_hifi_libraries(shared workload gpu shaders procedural graphics material-networking model-networking script-engine render render-utils image qml ui pointers)
|
||||||
include_hifi_library_headers(networking)
|
include_hifi_library_headers(networking)
|
||||||
include_hifi_library_headers(gl)
|
include_hifi_library_headers(gl)
|
||||||
include_hifi_library_headers(ktx)
|
include_hifi_library_headers(ktx)
|
||||||
|
|
|
@ -6,4 +6,4 @@ include_hifi_library_headers(fbx)
|
||||||
include_hifi_library_headers(gpu)
|
include_hifi_library_headers(gpu)
|
||||||
include_hifi_library_headers(image)
|
include_hifi_library_headers(image)
|
||||||
include_hifi_library_headers(ktx)
|
include_hifi_library_headers(ktx)
|
||||||
link_hifi_libraries(shared shaders networking octree avatars graphics model-networking)
|
link_hifi_libraries(shared shaders networking octree avatars graphics material-networking model-networking)
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
#include "MaterialMappingMode.h"
|
#include "MaterialMappingMode.h"
|
||||||
#include <model-networking/ModelCache.h>
|
#include <model-networking/ModelCache.h>
|
||||||
#include <model-networking/MaterialCache.h>
|
#include <material-networking/MaterialCache.h>
|
||||||
|
|
||||||
class MaterialEntityItem : public EntityItem {
|
class MaterialEntityItem : public EntityItem {
|
||||||
using Pointer = std::shared_ptr<MaterialEntityItem>;
|
using Pointer = std::shared_ptr<MaterialEntityItem>;
|
||||||
|
|
|
@ -1326,7 +1326,7 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
|
||||||
hfmModel.meshExtents.reset();
|
hfmModel.meshExtents.reset();
|
||||||
|
|
||||||
// Create the Material Library
|
// Create the Material Library
|
||||||
consolidateHFMMaterials(mapping);
|
consolidateHFMMaterials();
|
||||||
|
|
||||||
// We can't allow the scaling of a given image to different sizes, because the hash used for the KTX cache is based on the original image
|
// We can't allow the scaling of a given image to different sizes, because the hash used for the KTX cache is based on the original image
|
||||||
// Allowing scaling of the same image to different sizes would cause different KTX files to target the same cache key
|
// Allowing scaling of the same image to different sizes would cause different KTX files to target the same cache key
|
||||||
|
|
|
@ -153,7 +153,7 @@ public:
|
||||||
QHash<QString, HFMMaterial> _hfmMaterials;
|
QHash<QString, HFMMaterial> _hfmMaterials;
|
||||||
QHash<QString, MaterialParam> _materialParams;
|
QHash<QString, MaterialParam> _materialParams;
|
||||||
|
|
||||||
void consolidateHFMMaterials(const QVariantHash& mapping);
|
void consolidateHFMMaterials();
|
||||||
|
|
||||||
bool _loadLightmaps { true };
|
bool _loadLightmaps { true };
|
||||||
float _lightmapOffset { 0.0f };
|
float _lightmapOffset { 0.0f };
|
||||||
|
|
|
@ -75,15 +75,7 @@ HFMTexture FBXSerializer::getTexture(const QString& textureID, const QString& ma
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FBXSerializer::consolidateHFMMaterials(const QVariantHash& mapping) {
|
void FBXSerializer::consolidateHFMMaterials() {
|
||||||
QJsonObject materialMap;
|
|
||||||
if (mapping.contains("materialMap")) {
|
|
||||||
QByteArray materialMapValue = mapping.value("materialMap").toByteArray();
|
|
||||||
materialMap = QJsonDocument::fromJson(materialMapValue).object();
|
|
||||||
if (materialMap.isEmpty()) {
|
|
||||||
qCDebug(modelformat) << "fbx Material Map found but did not produce valid JSON:" << materialMapValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (QHash<QString, HFMMaterial>::iterator it = _hfmMaterials.begin(); it != _hfmMaterials.end(); it++) {
|
for (QHash<QString, HFMMaterial>::iterator it = _hfmMaterials.begin(); it != _hfmMaterials.end(); it++) {
|
||||||
HFMMaterial& material = (*it);
|
HFMMaterial& material = (*it);
|
||||||
|
|
||||||
|
@ -266,23 +258,6 @@ void FBXSerializer::consolidateHFMMaterials(const QVariantHash& mapping) {
|
||||||
}
|
}
|
||||||
qCDebug(modelformat) << " fbx material Name:" << material.name;
|
qCDebug(modelformat) << " fbx material Name:" << material.name;
|
||||||
|
|
||||||
if (materialMap.contains(material.name)) {
|
|
||||||
QJsonObject materialOptions = materialMap.value(material.name).toObject();
|
|
||||||
qCDebug(modelformat) << "Mapping fbx material:" << material.name << " with HifiMaterial: " << materialOptions;
|
|
||||||
|
|
||||||
if (materialOptions.contains("scattering")) {
|
|
||||||
float scattering = (float) materialOptions.value("scattering").toDouble();
|
|
||||||
material._material->setScattering(scattering);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (materialOptions.contains("scatteringMap")) {
|
|
||||||
QByteArray scatteringMap = materialOptions.value("scatteringMap").toVariant().toByteArray();
|
|
||||||
material.scatteringTexture = HFMTexture();
|
|
||||||
material.scatteringTexture.name = material.name + ".scatteringMap";
|
|
||||||
material.scatteringTexture.filename = scatteringMap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (material.opacity <= 0.0f) {
|
if (material.opacity <= 0.0f) {
|
||||||
material._material->setOpacity(1.0f);
|
material._material->setOpacity(1.0f);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -892,6 +892,23 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const QUrl& url) {
|
||||||
for (int n = 0; n < colors.size() - 3; n += stride) {
|
for (int n = 0; n < colors.size() - 3; n += stride) {
|
||||||
mesh.colors.push_back(glm::vec3(colors[n], colors[n + 1], colors[n + 2]));
|
mesh.colors.push_back(glm::vec3(colors[n], colors[n + 1], colors[n + 2]));
|
||||||
}
|
}
|
||||||
|
} else if (key == "TANGENT") {
|
||||||
|
QVector<float> tangents;
|
||||||
|
success = addArrayOfType(buffer.blob,
|
||||||
|
bufferview.byteOffset + accBoffset,
|
||||||
|
accessor.count,
|
||||||
|
tangents,
|
||||||
|
accessor.type,
|
||||||
|
accessor.componentType);
|
||||||
|
if (!success) {
|
||||||
|
qWarning(modelformat) << "There was a problem reading glTF TANGENT data for model " << _url;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int stride = (accessor.type == GLTFAccessorType::VEC4) ? 4 : 3;
|
||||||
|
for (int n = 0; n < tangents.size() - 3; n += stride) {
|
||||||
|
float tanW = stride == 4 ? tangents[n + 3] : 1;
|
||||||
|
mesh.tangents.push_back(glm::vec3(tanW * tangents[n], tangents[n + 1], tangents[n + 2]));
|
||||||
|
}
|
||||||
} else if (key == "TEXCOORD_0") {
|
} else if (key == "TEXCOORD_0") {
|
||||||
QVector<float> texcoords;
|
QVector<float> texcoords;
|
||||||
success = addArrayOfType(buffer.blob,
|
success = addArrayOfType(buffer.blob,
|
||||||
|
@ -931,7 +948,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const QUrl& url) {
|
||||||
}
|
}
|
||||||
mesh.parts.push_back(part);
|
mesh.parts.push_back(part);
|
||||||
|
|
||||||
// populate the texture coordenates if they don't exist
|
// populate the texture coordinates if they don't exist
|
||||||
if (mesh.texCoords.size() == 0) {
|
if (mesh.texCoords.size() == 0) {
|
||||||
for (int i = 0; i < part.triangleIndices.size(); i++) mesh.texCoords.push_back(glm::vec2(0.0, 1.0));
|
for (int i = 0; i < part.triangleIndices.size(); i++) mesh.texCoords.push_back(glm::vec2(0.0, 1.0));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
set(TARGET_NAME graphics-scripting)
|
set(TARGET_NAME graphics-scripting)
|
||||||
setup_hifi_library()
|
setup_hifi_library()
|
||||||
link_hifi_libraries(shared networking graphics fbx image model-networking script-engine)
|
link_hifi_libraries(shared networking graphics fbx image material-networking model-networking script-engine)
|
||||||
include_hifi_library_headers(gpu)
|
include_hifi_library_headers(gpu)
|
||||||
|
|
5
libraries/material-networking/CMakeLists.txt
Normal file
5
libraries/material-networking/CMakeLists.txt
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
set(TARGET_NAME material-networking)
|
||||||
|
setup_hifi_library()
|
||||||
|
link_hifi_libraries(shared shaders networking graphics ktx image gl)
|
||||||
|
include_hifi_library_headers(gpu)
|
||||||
|
include_hifi_library_headers(hfm)
|
|
@ -425,5 +425,311 @@ QSharedPointer<Resource> MaterialCache::createResource(const QUrl& url) {
|
||||||
}
|
}
|
||||||
|
|
||||||
QSharedPointer<Resource> MaterialCache::createResourceCopy(const QSharedPointer<Resource>& resource) {
|
QSharedPointer<Resource> MaterialCache::createResourceCopy(const QSharedPointer<Resource>& resource) {
|
||||||
return QSharedPointer<Resource>(new NetworkMaterialResource(*resource.staticCast<NetworkMaterialResource>().data()), &Resource::deleter);
|
return QSharedPointer<Resource>(new NetworkMaterialResource(*resource.staticCast<NetworkMaterialResource>()), &Resource::deleter);
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkMaterial::NetworkMaterial(const NetworkMaterial& m) :
|
||||||
|
Material(m),
|
||||||
|
_textures(m._textures),
|
||||||
|
_albedoTransform(m._albedoTransform),
|
||||||
|
_lightmapTransform(m._lightmapTransform),
|
||||||
|
_lightmapParams(m._lightmapParams),
|
||||||
|
_isOriginal(m._isOriginal)
|
||||||
|
{}
|
||||||
|
|
||||||
|
const QString NetworkMaterial::NO_TEXTURE = QString();
|
||||||
|
|
||||||
|
const QString& NetworkMaterial::getTextureName(MapChannel channel) {
|
||||||
|
if (_textures[channel].texture) {
|
||||||
|
return _textures[channel].name;
|
||||||
|
}
|
||||||
|
return NO_TEXTURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
QUrl NetworkMaterial::getTextureUrl(const QUrl& baseUrl, const HFMTexture& texture) {
|
||||||
|
if (texture.content.isEmpty()) {
|
||||||
|
// External file: search relative to the baseUrl, in case filename is relative
|
||||||
|
return baseUrl.resolved(QUrl(texture.filename));
|
||||||
|
} else {
|
||||||
|
// Inlined file: cache under the fbx file to avoid namespace clashes
|
||||||
|
// NOTE: We cannot resolve the path because filename may be an absolute path
|
||||||
|
assert(texture.filename.size() > 0);
|
||||||
|
auto baseUrlStripped = baseUrl.toDisplayString(QUrl::RemoveFragment | QUrl::RemoveQuery | QUrl::RemoveUserInfo);
|
||||||
|
if (texture.filename.at(0) == '/') {
|
||||||
|
return baseUrlStripped + texture.filename;
|
||||||
|
} else {
|
||||||
|
return baseUrlStripped + '/' + texture.filename;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
graphics::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& baseUrl, const HFMTexture& hfmTexture,
|
||||||
|
image::TextureUsage::Type type, MapChannel channel) {
|
||||||
|
|
||||||
|
if (baseUrl.isEmpty()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto url = getTextureUrl(baseUrl, hfmTexture);
|
||||||
|
const auto texture = DependencyManager::get<TextureCache>()->getTexture(url, type, hfmTexture.content, hfmTexture.maxNumPixels, hfmTexture.sourceChannel);
|
||||||
|
_textures[channel] = Texture { hfmTexture.name, texture };
|
||||||
|
|
||||||
|
auto map = std::make_shared<graphics::TextureMap>();
|
||||||
|
if (texture) {
|
||||||
|
map->setTextureSource(texture->_textureSource);
|
||||||
|
}
|
||||||
|
map->setTextureTransform(hfmTexture.transform);
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
graphics::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& url, image::TextureUsage::Type type, MapChannel channel) {
|
||||||
|
auto textureCache = DependencyManager::get<TextureCache>();
|
||||||
|
if (textureCache && !url.isEmpty()) {
|
||||||
|
auto texture = textureCache->getTexture(url, type);
|
||||||
|
_textures[channel].texture = texture;
|
||||||
|
|
||||||
|
auto map = std::make_shared<graphics::TextureMap>();
|
||||||
|
if (texture) {
|
||||||
|
map->setTextureSource(texture->_textureSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkMaterial::setAlbedoMap(const QUrl& url, bool useAlphaChannel) {
|
||||||
|
auto map = fetchTextureMap(url, image::TextureUsage::ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP);
|
||||||
|
if (map) {
|
||||||
|
map->setUseAlphaChannel(useAlphaChannel);
|
||||||
|
setTextureMap(MapChannel::ALBEDO_MAP, map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkMaterial::setNormalMap(const QUrl& url, bool isBumpmap) {
|
||||||
|
auto map = fetchTextureMap(url, isBumpmap ? image::TextureUsage::BUMP_TEXTURE : image::TextureUsage::NORMAL_TEXTURE, MapChannel::NORMAL_MAP);
|
||||||
|
if (map) {
|
||||||
|
setTextureMap(MapChannel::NORMAL_MAP, map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkMaterial::setRoughnessMap(const QUrl& url, bool isGloss) {
|
||||||
|
auto map = fetchTextureMap(url, isGloss ? image::TextureUsage::GLOSS_TEXTURE : image::TextureUsage::ROUGHNESS_TEXTURE, MapChannel::ROUGHNESS_MAP);
|
||||||
|
if (map) {
|
||||||
|
setTextureMap(MapChannel::ROUGHNESS_MAP, map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkMaterial::setMetallicMap(const QUrl& url, bool isSpecular) {
|
||||||
|
auto map = fetchTextureMap(url, isSpecular ? image::TextureUsage::SPECULAR_TEXTURE : image::TextureUsage::METALLIC_TEXTURE, MapChannel::METALLIC_MAP);
|
||||||
|
if (map) {
|
||||||
|
setTextureMap(MapChannel::METALLIC_MAP, map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkMaterial::setOcclusionMap(const QUrl& url) {
|
||||||
|
auto map = fetchTextureMap(url, image::TextureUsage::OCCLUSION_TEXTURE, MapChannel::OCCLUSION_MAP);
|
||||||
|
if (map) {
|
||||||
|
setTextureMap(MapChannel::OCCLUSION_MAP, map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkMaterial::setEmissiveMap(const QUrl& url) {
|
||||||
|
auto map = fetchTextureMap(url, image::TextureUsage::EMISSIVE_TEXTURE, MapChannel::EMISSIVE_MAP);
|
||||||
|
if (map) {
|
||||||
|
setTextureMap(MapChannel::EMISSIVE_MAP, map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkMaterial::setScatteringMap(const QUrl& url) {
|
||||||
|
auto map = fetchTextureMap(url, image::TextureUsage::SCATTERING_TEXTURE, MapChannel::SCATTERING_MAP);
|
||||||
|
if (map) {
|
||||||
|
setTextureMap(MapChannel::SCATTERING_MAP, map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkMaterial::setLightmapMap(const QUrl& url) {
|
||||||
|
auto map = fetchTextureMap(url, image::TextureUsage::LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP);
|
||||||
|
if (map) {
|
||||||
|
//map->setTextureTransform(_lightmapTransform);
|
||||||
|
//map->setLightmapOffsetScale(_lightmapParams.x, _lightmapParams.y);
|
||||||
|
setTextureMap(MapChannel::LIGHTMAP_MAP, map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkMaterial::NetworkMaterial(const HFMMaterial& material, const QUrl& textureBaseUrl) :
|
||||||
|
graphics::Material(*material._material),
|
||||||
|
_textures(MapChannel::NUM_MAP_CHANNELS)
|
||||||
|
{
|
||||||
|
_name = material.name.toStdString();
|
||||||
|
if (!material.albedoTexture.filename.isEmpty()) {
|
||||||
|
auto map = fetchTextureMap(textureBaseUrl, material.albedoTexture, image::TextureUsage::ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP);
|
||||||
|
if (map) {
|
||||||
|
_albedoTransform = material.albedoTexture.transform;
|
||||||
|
map->setTextureTransform(_albedoTransform);
|
||||||
|
|
||||||
|
if (!material.opacityTexture.filename.isEmpty()) {
|
||||||
|
if (material.albedoTexture.filename == material.opacityTexture.filename) {
|
||||||
|
// Best case scenario, just indicating that the albedo map contains transparency
|
||||||
|
// TODO: Different albedo/opacity maps are not currently supported
|
||||||
|
map->setUseAlphaChannel(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setTextureMap(MapChannel::ALBEDO_MAP, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!material.normalTexture.filename.isEmpty()) {
|
||||||
|
auto type = (material.normalTexture.isBumpmap ? image::TextureUsage::BUMP_TEXTURE : image::TextureUsage::NORMAL_TEXTURE);
|
||||||
|
auto map = fetchTextureMap(textureBaseUrl, material.normalTexture, type, MapChannel::NORMAL_MAP);
|
||||||
|
setTextureMap(MapChannel::NORMAL_MAP, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!material.roughnessTexture.filename.isEmpty()) {
|
||||||
|
auto map = fetchTextureMap(textureBaseUrl, material.roughnessTexture, image::TextureUsage::ROUGHNESS_TEXTURE, MapChannel::ROUGHNESS_MAP);
|
||||||
|
setTextureMap(MapChannel::ROUGHNESS_MAP, map);
|
||||||
|
} else if (!material.glossTexture.filename.isEmpty()) {
|
||||||
|
auto map = fetchTextureMap(textureBaseUrl, material.glossTexture, image::TextureUsage::GLOSS_TEXTURE, MapChannel::ROUGHNESS_MAP);
|
||||||
|
setTextureMap(MapChannel::ROUGHNESS_MAP, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!material.metallicTexture.filename.isEmpty()) {
|
||||||
|
auto map = fetchTextureMap(textureBaseUrl, material.metallicTexture, image::TextureUsage::METALLIC_TEXTURE, MapChannel::METALLIC_MAP);
|
||||||
|
setTextureMap(MapChannel::METALLIC_MAP, map);
|
||||||
|
} else if (!material.specularTexture.filename.isEmpty()) {
|
||||||
|
auto map = fetchTextureMap(textureBaseUrl, material.specularTexture, image::TextureUsage::SPECULAR_TEXTURE, MapChannel::METALLIC_MAP);
|
||||||
|
setTextureMap(MapChannel::METALLIC_MAP, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!material.occlusionTexture.filename.isEmpty()) {
|
||||||
|
auto map = fetchTextureMap(textureBaseUrl, material.occlusionTexture, image::TextureUsage::OCCLUSION_TEXTURE, MapChannel::OCCLUSION_MAP);
|
||||||
|
if (map) {
|
||||||
|
map->setTextureTransform(material.occlusionTexture.transform);
|
||||||
|
}
|
||||||
|
setTextureMap(MapChannel::OCCLUSION_MAP, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!material.emissiveTexture.filename.isEmpty()) {
|
||||||
|
auto map = fetchTextureMap(textureBaseUrl, material.emissiveTexture, image::TextureUsage::EMISSIVE_TEXTURE, MapChannel::EMISSIVE_MAP);
|
||||||
|
setTextureMap(MapChannel::EMISSIVE_MAP, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!material.scatteringTexture.filename.isEmpty()) {
|
||||||
|
auto map = fetchTextureMap(textureBaseUrl, material.scatteringTexture, image::TextureUsage::SCATTERING_TEXTURE, MapChannel::SCATTERING_MAP);
|
||||||
|
setTextureMap(MapChannel::SCATTERING_MAP, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!material.lightmapTexture.filename.isEmpty()) {
|
||||||
|
auto map = fetchTextureMap(textureBaseUrl, material.lightmapTexture, image::TextureUsage::LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP);
|
||||||
|
if (map) {
|
||||||
|
_lightmapTransform = material.lightmapTexture.transform;
|
||||||
|
_lightmapParams = material.lightmapParams;
|
||||||
|
map->setTextureTransform(_lightmapTransform);
|
||||||
|
map->setLightmapOffsetScale(_lightmapParams.x, _lightmapParams.y);
|
||||||
|
}
|
||||||
|
setTextureMap(MapChannel::LIGHTMAP_MAP, map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkMaterial::setTextures(const QVariantMap& textureMap) {
|
||||||
|
_isOriginal = false;
|
||||||
|
|
||||||
|
const auto& albedoName = getTextureName(MapChannel::ALBEDO_MAP);
|
||||||
|
const auto& normalName = getTextureName(MapChannel::NORMAL_MAP);
|
||||||
|
const auto& roughnessName = getTextureName(MapChannel::ROUGHNESS_MAP);
|
||||||
|
const auto& metallicName = getTextureName(MapChannel::METALLIC_MAP);
|
||||||
|
const auto& occlusionName = getTextureName(MapChannel::OCCLUSION_MAP);
|
||||||
|
const auto& emissiveName = getTextureName(MapChannel::EMISSIVE_MAP);
|
||||||
|
const auto& lightmapName = getTextureName(MapChannel::LIGHTMAP_MAP);
|
||||||
|
const auto& scatteringName = getTextureName(MapChannel::SCATTERING_MAP);
|
||||||
|
|
||||||
|
if (!albedoName.isEmpty()) {
|
||||||
|
auto url = textureMap.contains(albedoName) ? textureMap[albedoName].toUrl() : QUrl();
|
||||||
|
auto map = fetchTextureMap(url, image::TextureUsage::ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP);
|
||||||
|
if (map) {
|
||||||
|
map->setTextureTransform(_albedoTransform);
|
||||||
|
// when reassigning the albedo texture we also check for the alpha channel used as opacity
|
||||||
|
map->setUseAlphaChannel(true);
|
||||||
|
}
|
||||||
|
setTextureMap(MapChannel::ALBEDO_MAP, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!normalName.isEmpty()) {
|
||||||
|
auto url = textureMap.contains(normalName) ? textureMap[normalName].toUrl() : QUrl();
|
||||||
|
auto map = fetchTextureMap(url, image::TextureUsage::NORMAL_TEXTURE, MapChannel::NORMAL_MAP);
|
||||||
|
setTextureMap(MapChannel::NORMAL_MAP, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!roughnessName.isEmpty()) {
|
||||||
|
auto url = textureMap.contains(roughnessName) ? textureMap[roughnessName].toUrl() : QUrl();
|
||||||
|
// FIXME: If passing a gloss map instead of a roughmap how do we know?
|
||||||
|
auto map = fetchTextureMap(url, image::TextureUsage::ROUGHNESS_TEXTURE, MapChannel::ROUGHNESS_MAP);
|
||||||
|
setTextureMap(MapChannel::ROUGHNESS_MAP, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!metallicName.isEmpty()) {
|
||||||
|
auto url = textureMap.contains(metallicName) ? textureMap[metallicName].toUrl() : QUrl();
|
||||||
|
// FIXME: If passing a specular map instead of a metallic how do we know?
|
||||||
|
auto map = fetchTextureMap(url, image::TextureUsage::METALLIC_TEXTURE, MapChannel::METALLIC_MAP);
|
||||||
|
setTextureMap(MapChannel::METALLIC_MAP, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!occlusionName.isEmpty()) {
|
||||||
|
auto url = textureMap.contains(occlusionName) ? textureMap[occlusionName].toUrl() : QUrl();
|
||||||
|
// FIXME: we need to handle the occlusion map transform here
|
||||||
|
auto map = fetchTextureMap(url, image::TextureUsage::OCCLUSION_TEXTURE, MapChannel::OCCLUSION_MAP);
|
||||||
|
setTextureMap(MapChannel::OCCLUSION_MAP, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!emissiveName.isEmpty()) {
|
||||||
|
auto url = textureMap.contains(emissiveName) ? textureMap[emissiveName].toUrl() : QUrl();
|
||||||
|
auto map = fetchTextureMap(url, image::TextureUsage::EMISSIVE_TEXTURE, MapChannel::EMISSIVE_MAP);
|
||||||
|
setTextureMap(MapChannel::EMISSIVE_MAP, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!scatteringName.isEmpty()) {
|
||||||
|
auto url = textureMap.contains(scatteringName) ? textureMap[scatteringName].toUrl() : QUrl();
|
||||||
|
auto map = fetchTextureMap(url, image::TextureUsage::SCATTERING_TEXTURE, MapChannel::SCATTERING_MAP);
|
||||||
|
setTextureMap(MapChannel::SCATTERING_MAP, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lightmapName.isEmpty()) {
|
||||||
|
auto url = textureMap.contains(lightmapName) ? textureMap[lightmapName].toUrl() : QUrl();
|
||||||
|
auto map = fetchTextureMap(url, image::TextureUsage::LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP);
|
||||||
|
if (map) {
|
||||||
|
map->setTextureTransform(_lightmapTransform);
|
||||||
|
map->setLightmapOffsetScale(_lightmapParams.x, _lightmapParams.y);
|
||||||
|
}
|
||||||
|
setTextureMap(MapChannel::LIGHTMAP_MAP, map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NetworkMaterial::isMissingTexture() {
|
||||||
|
for (auto& networkTexture : _textures) {
|
||||||
|
auto& texture = networkTexture.texture;
|
||||||
|
if (!texture) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Failed texture downloads need to be considered as 'loaded'
|
||||||
|
// or the object will never fade in
|
||||||
|
bool finished = texture->isFailed() || (texture->isLoaded() && texture->getGPUTexture() && texture->getGPUTexture()->isDefined());
|
||||||
|
if (!finished) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkMaterial::checkResetOpacityMap() {
|
||||||
|
// If material textures are loaded, check the material translucency
|
||||||
|
// FIXME: This should not be done here. The opacity map should already be reset in Material::setTextureMap.
|
||||||
|
// However, currently that code can be called before the albedo map is defined, so resetOpacityMap will fail.
|
||||||
|
// Geometry::areTexturesLoaded() is called repeatedly until it returns true, so we do the check here for now
|
||||||
|
const auto& albedoTexture = _textures[NetworkMaterial::MapChannel::ALBEDO_MAP];
|
||||||
|
if (albedoTexture.texture) {
|
||||||
|
resetOpacityMap();
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,117 @@
|
||||||
|
//
|
||||||
|
// Created by Sam Gondelman on 2/9/2018
|
||||||
|
// Copyright 2018 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
#ifndef hifi_MaterialCache_h
|
||||||
|
#define hifi_MaterialCache_h
|
||||||
|
|
||||||
|
#include "glm/glm.hpp"
|
||||||
|
|
||||||
|
#include <ResourceCache.h>
|
||||||
|
#include <graphics/Material.h>
|
||||||
|
#include <hfm/HFM.h>
|
||||||
|
|
||||||
|
#include "TextureCache.h"
|
||||||
|
|
||||||
|
class NetworkMaterial : public graphics::Material {
|
||||||
|
public:
|
||||||
|
using MapChannel = graphics::Material::MapChannel;
|
||||||
|
|
||||||
|
NetworkMaterial() : _textures(MapChannel::NUM_MAP_CHANNELS) {}
|
||||||
|
NetworkMaterial(const HFMMaterial& material, const QUrl& textureBaseUrl);
|
||||||
|
NetworkMaterial(const NetworkMaterial& material);
|
||||||
|
|
||||||
|
void setAlbedoMap(const QUrl& url, bool useAlphaChannel);
|
||||||
|
void setNormalMap(const QUrl& url, bool isBumpmap);
|
||||||
|
void setRoughnessMap(const QUrl& url, bool isGloss);
|
||||||
|
void setMetallicMap(const QUrl& url, bool isSpecular);
|
||||||
|
void setOcclusionMap(const QUrl& url);
|
||||||
|
void setEmissiveMap(const QUrl& url);
|
||||||
|
void setScatteringMap(const QUrl& url);
|
||||||
|
void setLightmapMap(const QUrl& url);
|
||||||
|
|
||||||
|
bool isMissingTexture();
|
||||||
|
void checkResetOpacityMap();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
friend class Geometry;
|
||||||
|
|
||||||
|
class Texture {
|
||||||
|
public:
|
||||||
|
QString name;
|
||||||
|
NetworkTexturePointer texture;
|
||||||
|
};
|
||||||
|
using Textures = std::vector<Texture>;
|
||||||
|
|
||||||
|
Textures _textures;
|
||||||
|
|
||||||
|
static const QString NO_TEXTURE;
|
||||||
|
const QString& getTextureName(MapChannel channel);
|
||||||
|
|
||||||
|
void setTextures(const QVariantMap& textureMap);
|
||||||
|
|
||||||
|
const bool& isOriginal() const { return _isOriginal; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Helpers for the ctors
|
||||||
|
QUrl getTextureUrl(const QUrl& baseUrl, const HFMTexture& hfmTexture);
|
||||||
|
graphics::TextureMapPointer fetchTextureMap(const QUrl& baseUrl, const HFMTexture& hfmTexture,
|
||||||
|
image::TextureUsage::Type type, MapChannel channel);
|
||||||
|
graphics::TextureMapPointer fetchTextureMap(const QUrl& url, image::TextureUsage::Type type, MapChannel channel);
|
||||||
|
|
||||||
|
Transform _albedoTransform;
|
||||||
|
Transform _lightmapTransform;
|
||||||
|
vec2 _lightmapParams;
|
||||||
|
|
||||||
|
bool _isOriginal { true };
|
||||||
|
};
|
||||||
|
|
||||||
|
class NetworkMaterialResource : public Resource {
|
||||||
|
public:
|
||||||
|
NetworkMaterialResource() : Resource() {}
|
||||||
|
NetworkMaterialResource(const QUrl& url);
|
||||||
|
|
||||||
|
QString getType() const override { return "NetworkMaterial"; }
|
||||||
|
|
||||||
|
virtual void downloadFinished(const QByteArray& data) override;
|
||||||
|
|
||||||
|
typedef struct ParsedMaterials {
|
||||||
|
uint version { 1 };
|
||||||
|
std::vector<std::string> names;
|
||||||
|
std::unordered_map<std::string, std::shared_ptr<NetworkMaterial>> networkMaterials;
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
version = 1;
|
||||||
|
names.clear();
|
||||||
|
networkMaterials.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
} ParsedMaterials;
|
||||||
|
|
||||||
|
ParsedMaterials parsedMaterials;
|
||||||
|
|
||||||
|
static ParsedMaterials parseJSONMaterials(const QJsonDocument& materialJSON, const QUrl& baseUrl);
|
||||||
|
static std::pair<std::string, std::shared_ptr<NetworkMaterial>> parseJSONMaterial(const QJsonObject& materialJSON, const QUrl& baseUrl);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static bool parseJSONColor(const QJsonValue& array, glm::vec3& color, bool& isSRGB);
|
||||||
|
};
|
||||||
|
|
||||||
|
using NetworkMaterialResourcePointer = QSharedPointer<NetworkMaterialResource>;
|
||||||
|
using MaterialMapping = std::vector<std::pair<std::string, NetworkMaterialResourcePointer>>;
|
||||||
|
|
||||||
|
class MaterialCache : public ResourceCache {
|
||||||
|
public:
|
||||||
|
static MaterialCache& instance();
|
||||||
|
|
||||||
|
NetworkMaterialResourcePointer getMaterial(const QUrl& url);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual QSharedPointer<Resource> createResource(const QUrl& url) override;
|
||||||
|
QSharedPointer<Resource> createResourceCopy(const QSharedPointer<Resource>& resource) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,11 @@
|
||||||
|
//
|
||||||
|
// Created by Sam Gondelman on 2/7/2019
|
||||||
|
// Copyright 2019 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "MaterialNetworkingLogging.h"
|
||||||
|
|
||||||
|
Q_LOGGING_CATEGORY(materialnetworking, "hifi.gpu-material-network")
|
|
@ -0,0 +1,11 @@
|
||||||
|
//
|
||||||
|
// Created by Sam Gondelman on 2/7/2019
|
||||||
|
// Copyright 2019 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
|
||||||
|
Q_DECLARE_LOGGING_CATEGORY(materialnetworking)
|
|
@ -29,5 +29,5 @@ QSharedPointer<Resource> ShaderCache::createResource(const QUrl& url) {
|
||||||
}
|
}
|
||||||
|
|
||||||
QSharedPointer<Resource> ShaderCache::createResourceCopy(const QSharedPointer<Resource>& resource) {
|
QSharedPointer<Resource> ShaderCache::createResourceCopy(const QSharedPointer<Resource>& resource) {
|
||||||
return QSharedPointer<Resource>(new NetworkShader(*resource.staticCast<NetworkShader>().data()), &Resource::deleter);
|
return QSharedPointer<Resource>(new NetworkShader(*resource.staticCast<NetworkShader>()), &Resource::deleter);
|
||||||
}
|
}
|
|
@ -44,7 +44,7 @@
|
||||||
#include <Profile.h>
|
#include <Profile.h>
|
||||||
|
|
||||||
#include "NetworkLogging.h"
|
#include "NetworkLogging.h"
|
||||||
#include "ModelNetworkingLogging.h"
|
#include "MaterialNetworkingLogging.h"
|
||||||
#include "NetworkingConstants.h"
|
#include "NetworkingConstants.h"
|
||||||
#include <Trace.h>
|
#include <Trace.h>
|
||||||
#include <StatTracker.h>
|
#include <StatTracker.h>
|
||||||
|
@ -198,16 +198,16 @@ public:
|
||||||
namespace std {
|
namespace std {
|
||||||
template <>
|
template <>
|
||||||
struct hash<QByteArray> {
|
struct hash<QByteArray> {
|
||||||
size_t operator()(const QByteArray& a) const {
|
size_t operator()(const QByteArray& byteArray) const {
|
||||||
return qHash(a);
|
return qHash(byteArray);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct hash<TextureExtra> {
|
struct hash<TextureExtra> {
|
||||||
size_t operator()(const TextureExtra& a) const {
|
size_t operator()(const TextureExtra& textureExtra) const {
|
||||||
size_t result = 0;
|
size_t result = 0;
|
||||||
hash_combine(result, (int)a.type, a.content, a.maxNumPixels, (int)a.sourceChannel);
|
hash_combine(result, (int)textureExtra.type, textureExtra.content, textureExtra.maxNumPixels, (int)textureExtra.sourceChannel);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -329,15 +329,14 @@ QSharedPointer<Resource> TextureCache::createResource(const QUrl& url) {
|
||||||
}
|
}
|
||||||
|
|
||||||
QSharedPointer<Resource> TextureCache::createResourceCopy(const QSharedPointer<Resource>& resource) {
|
QSharedPointer<Resource> TextureCache::createResourceCopy(const QSharedPointer<Resource>& resource) {
|
||||||
return QSharedPointer<Resource>(new NetworkTexture(*resource.staticCast<NetworkTexture>().data()), &Resource::deleter);
|
return QSharedPointer<Resource>(new NetworkTexture(*resource.staticCast<NetworkTexture>()), &Resource::deleter);
|
||||||
}
|
}
|
||||||
|
|
||||||
int networkTexturePointerMetaTypeId = qRegisterMetaType<QWeakPointer<NetworkTexture>>();
|
int networkTexturePointerMetaTypeId = qRegisterMetaType<QWeakPointer<NetworkTexture>>();
|
||||||
|
|
||||||
NetworkTexture::NetworkTexture(const QUrl& url, bool resourceTexture) :
|
NetworkTexture::NetworkTexture(const QUrl& url, bool resourceTexture) :
|
||||||
Resource(url),
|
Resource(url),
|
||||||
Texture(),
|
Texture()
|
||||||
_maxNumPixels(100)
|
|
||||||
{
|
{
|
||||||
if (resourceTexture) {
|
if (resourceTexture) {
|
||||||
_textureSource = std::make_shared<gpu::TextureSource>(url);
|
_textureSource = std::make_shared<gpu::TextureSource>(url);
|
||||||
|
@ -955,7 +954,7 @@ void NetworkTexture::handleFinishedInitialLoad() {
|
||||||
cache::FilePointer file;
|
cache::FilePointer file;
|
||||||
auto& ktxCache = textureCache->_ktxCache;
|
auto& ktxCache = textureCache->_ktxCache;
|
||||||
if (!memKtx || !(file = ktxCache->writeFile(data, KTXCache::Metadata(filename, length)))) {
|
if (!memKtx || !(file = ktxCache->writeFile(data, KTXCache::Metadata(filename, length)))) {
|
||||||
qCWarning(modelnetworking) << url << " failed to write cache file";
|
qCWarning(materialnetworking) << url << " failed to write cache file";
|
||||||
QMetaObject::invokeMethod(resource.data(), "setImage",
|
QMetaObject::invokeMethod(resource.data(), "setImage",
|
||||||
Q_ARG(gpu::TexturePointer, nullptr),
|
Q_ARG(gpu::TexturePointer, nullptr),
|
||||||
Q_ARG(int, 0),
|
Q_ARG(int, 0),
|
||||||
|
@ -1145,7 +1144,7 @@ void ImageReader::listSupportedImageFormats() {
|
||||||
static std::once_flag once;
|
static std::once_flag once;
|
||||||
std::call_once(once, []{
|
std::call_once(once, []{
|
||||||
auto supportedFormats = QImageReader::supportedImageFormats();
|
auto supportedFormats = QImageReader::supportedImageFormats();
|
||||||
qCDebug(modelnetworking) << "List of supported Image formats:" << supportedFormats.join(", ");
|
qCDebug(materialnetworking) << "List of supported Image formats:" << supportedFormats.join(", ");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1194,7 +1193,7 @@ void ImageReader::read() {
|
||||||
if (texture) {
|
if (texture) {
|
||||||
texture = textureCache->cacheTextureByHash(hash, texture);
|
texture = textureCache->cacheTextureByHash(hash, texture);
|
||||||
} else {
|
} else {
|
||||||
qCWarning(modelnetworking) << "Invalid cached KTX " << _url << " under hash " << hash.c_str() << ", recreating...";
|
qCWarning(materialnetworking) << "Invalid cached KTX " << _url << " under hash " << hash.c_str() << ", recreating...";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
set(TARGET_NAME model-baker)
|
set(TARGET_NAME model-baker)
|
||||||
setup_hifi_library()
|
setup_hifi_library()
|
||||||
|
|
||||||
link_hifi_libraries(shared task gpu graphics hfm)
|
link_hifi_libraries(shared shaders task gpu graphics hfm material-networking)
|
||||||
|
include_hifi_library_headers(networking)
|
||||||
include_hifi_library_headers(image)
|
include_hifi_library_headers(image)
|
||||||
|
include_hifi_library_headers(ktx)
|
|
@ -101,7 +101,7 @@ namespace baker {
|
||||||
|
|
||||||
class BuildModelTask {
|
class BuildModelTask {
|
||||||
public:
|
public:
|
||||||
using Input = VaryingSet5<hfm::Model::Pointer, std::vector<hfm::Mesh>, std::vector<hfm::Joint>, QMap<int, glm::quat> /*jointRotationOffsets*/, QHash<QString, int> /*jointIndices*/>;
|
using Input = VaryingSet5<hfm::Model::Pointer, std::vector<hfm::Mesh>, std::vector<hfm::Joint>, QMap<int, glm::quat>, QHash<QString, int>>;
|
||||||
using Output = hfm::Model::Pointer;
|
using Output = hfm::Model::Pointer;
|
||||||
using JobModel = Job::ModelIO<BuildModelTask, Input, Output>;
|
using JobModel = Job::ModelIO<BuildModelTask, Input, Output>;
|
||||||
|
|
||||||
|
@ -118,9 +118,9 @@ namespace baker {
|
||||||
class BakerEngineBuilder {
|
class BakerEngineBuilder {
|
||||||
public:
|
public:
|
||||||
using Input = VaryingSet2<hfm::Model::Pointer, QVariantHash>;
|
using Input = VaryingSet2<hfm::Model::Pointer, QVariantHash>;
|
||||||
using Output = hfm::Model::Pointer;
|
using Output = VaryingSet2<hfm::Model::Pointer, MaterialMapping>;
|
||||||
using JobModel = Task::ModelIO<BakerEngineBuilder, Input, Output>;
|
using JobModel = Task::ModelIO<BakerEngineBuilder, Input, Output>;
|
||||||
void build(JobModel& model, const Varying& input, Varying& hfmModelOut) {
|
void build(JobModel& model, const Varying& input, Varying& output) {
|
||||||
const auto& hfmModelIn = input.getN<Input>(0);
|
const auto& hfmModelIn = input.getN<Input>(0);
|
||||||
const auto& mapping = input.getN<Input>(1);
|
const auto& mapping = input.getN<Input>(1);
|
||||||
|
|
||||||
|
@ -154,13 +154,18 @@ namespace baker {
|
||||||
const auto jointRotationOffsets = jointInfoOut.getN<PrepareJointsTask::Output>(1);
|
const auto jointRotationOffsets = jointInfoOut.getN<PrepareJointsTask::Output>(1);
|
||||||
const auto jointIndices = jointInfoOut.getN<PrepareJointsTask::Output>(2);
|
const auto jointIndices = jointInfoOut.getN<PrepareJointsTask::Output>(2);
|
||||||
|
|
||||||
|
// Parse material mapping
|
||||||
|
const auto materialMapping = model.addJob<ParseMaterialMappingTask>("ParseMaterialMapping", mapping);
|
||||||
|
|
||||||
// Combine the outputs into a new hfm::Model
|
// Combine the outputs into a new hfm::Model
|
||||||
const auto buildBlendshapesInputs = BuildBlendshapesTask::Input(blendshapesPerMeshIn, normalsPerBlendshapePerMesh, tangentsPerBlendshapePerMesh).asVarying();
|
const auto buildBlendshapesInputs = BuildBlendshapesTask::Input(blendshapesPerMeshIn, normalsPerBlendshapePerMesh, tangentsPerBlendshapePerMesh).asVarying();
|
||||||
const auto blendshapesPerMeshOut = model.addJob<BuildBlendshapesTask>("BuildBlendshapes", buildBlendshapesInputs);
|
const auto blendshapesPerMeshOut = model.addJob<BuildBlendshapesTask>("BuildBlendshapes", buildBlendshapesInputs);
|
||||||
const auto buildMeshesInputs = BuildMeshesTask::Input(meshesIn, graphicsMeshes, normalsPerMesh, tangentsPerMesh, blendshapesPerMeshOut).asVarying();
|
const auto buildMeshesInputs = BuildMeshesTask::Input(meshesIn, graphicsMeshes, normalsPerMesh, tangentsPerMesh, blendshapesPerMeshOut).asVarying();
|
||||||
const auto meshesOut = model.addJob<BuildMeshesTask>("BuildMeshes", buildMeshesInputs);
|
const auto meshesOut = model.addJob<BuildMeshesTask>("BuildMeshes", buildMeshesInputs);
|
||||||
const auto buildModelInputs = BuildModelTask::Input(hfmModelIn, meshesOut, jointsOut, jointRotationOffsets, jointIndices).asVarying();
|
const auto buildModelInputs = BuildModelTask::Input(hfmModelIn, meshesOut, jointsOut, jointRotationOffsets, jointIndices).asVarying();
|
||||||
hfmModelOut = model.addJob<BuildModelTask>("BuildModel", buildModelInputs);
|
const auto hfmModelOut = model.addJob<BuildModelTask>("BuildModel", buildModelInputs);
|
||||||
|
|
||||||
|
output = Output(hfmModelOut, materialMapping);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -172,7 +177,8 @@ namespace baker {
|
||||||
|
|
||||||
void Baker::run() {
|
void Baker::run() {
|
||||||
_engine->run();
|
_engine->run();
|
||||||
hfmModel = _engine->getOutput().get<BakerEngineBuilder::Output>();
|
hfmModel = _engine->getOutput().get<BakerEngineBuilder::Output>().get0();
|
||||||
|
materialMapping = _engine->getOutput().get<BakerEngineBuilder::Output>().get1();
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
#include "Engine.h"
|
#include "Engine.h"
|
||||||
|
|
||||||
|
#include "ParseMaterialMappingTask.h"
|
||||||
|
|
||||||
namespace baker {
|
namespace baker {
|
||||||
class Baker {
|
class Baker {
|
||||||
public:
|
public:
|
||||||
|
@ -27,6 +29,7 @@ namespace baker {
|
||||||
|
|
||||||
// Outputs, available after run() is called
|
// Outputs, available after run() is called
|
||||||
hfm::Model::Pointer hfmModel;
|
hfm::Model::Pointer hfmModel;
|
||||||
|
MaterialMapping materialMapping;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
EnginePointer _engine;
|
EnginePointer _engine;
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
//
|
||||||
|
// Created by Sam Gondelman on 2/7/2019
|
||||||
|
// Copyright 2019 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "ParseMaterialMappingTask.h"
|
||||||
|
|
||||||
|
#include "ModelBakerLogging.h"
|
||||||
|
|
||||||
|
void ParseMaterialMappingTask::run(const baker::BakeContextPointer& context, const Input& mapping, Output& output) {
|
||||||
|
MaterialMapping materialMapping;
|
||||||
|
|
||||||
|
auto mappingIter = mapping.find("materialMap");
|
||||||
|
if (mappingIter != mapping.end()) {
|
||||||
|
QByteArray materialMapValue = mappingIter.value().toByteArray();
|
||||||
|
QJsonObject materialMap = QJsonDocument::fromJson(materialMapValue).object();
|
||||||
|
if (materialMap.isEmpty()) {
|
||||||
|
qCDebug(model_baker) << "Material Map found but did not produce valid JSON:" << materialMapValue;
|
||||||
|
} else {
|
||||||
|
auto mappingKeys = materialMap.keys();
|
||||||
|
for (auto mapping : mappingKeys) {
|
||||||
|
auto mappingJSON = materialMap[mapping];
|
||||||
|
if (mappingJSON.isObject()) {
|
||||||
|
auto mappingValue = mappingJSON.toObject();
|
||||||
|
|
||||||
|
// Old subsurface scattering mapping
|
||||||
|
{
|
||||||
|
auto scatteringIter = mappingValue.find("scattering");
|
||||||
|
auto scatteringMapIter = mappingValue.find("scatteringMap");
|
||||||
|
if (scatteringIter != mappingValue.end() || scatteringMapIter != mappingValue.end()) {
|
||||||
|
std::shared_ptr<NetworkMaterial> material = std::make_shared<NetworkMaterial>();
|
||||||
|
|
||||||
|
if (scatteringIter != mappingValue.end()) {
|
||||||
|
float scattering = (float)scatteringIter.value().toDouble();
|
||||||
|
material->setScattering(scattering);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scatteringMapIter != mappingValue.end()) {
|
||||||
|
QString scatteringMap = scatteringMapIter.value().toString();
|
||||||
|
material->setScatteringMap(scatteringMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
material->setDefaultFallthrough(true);
|
||||||
|
|
||||||
|
NetworkMaterialResourcePointer materialResource = NetworkMaterialResourcePointer(new NetworkMaterialResource(), [](NetworkMaterialResource* ptr) { ptr->deleteLater(); });
|
||||||
|
materialResource->moveToThread(qApp->thread());
|
||||||
|
materialResource->parsedMaterials.names.push_back("scattering");
|
||||||
|
materialResource->parsedMaterials.networkMaterials["scattering"] = material;
|
||||||
|
|
||||||
|
materialMapping.push_back(std::pair<std::string, NetworkMaterialResourcePointer>("mat::" + mapping.toStdString(), materialResource));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Material JSON description
|
||||||
|
{
|
||||||
|
NetworkMaterialResourcePointer materialResource = NetworkMaterialResourcePointer(new NetworkMaterialResource(), [](NetworkMaterialResource* ptr) { ptr->deleteLater(); });
|
||||||
|
materialResource->moveToThread(qApp->thread());
|
||||||
|
// TODO: add baseURL to allow FSTs to reference relative files next to them
|
||||||
|
materialResource->parsedMaterials = NetworkMaterialResource::parseJSONMaterials(QJsonDocument(mappingValue), QUrl());
|
||||||
|
materialMapping.push_back(std::pair<std::string, NetworkMaterialResourcePointer>(mapping.toStdString(), materialResource));
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (mappingJSON.isString()) {
|
||||||
|
auto mappingValue = mappingJSON.toString();
|
||||||
|
materialMapping.push_back(std::pair<std::string, NetworkMaterialResourcePointer>(mapping.toStdString(), MaterialCache::instance().getMaterial(mappingValue)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output = materialMapping;
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
//
|
||||||
|
// Created by Sam Gondelman on 2/7/2019
|
||||||
|
// Copyright 2019 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_ParseMaterialMappingTask_h
|
||||||
|
#define hifi_ParseMaterialMappingTask_h
|
||||||
|
|
||||||
|
#include <QHash>
|
||||||
|
|
||||||
|
#include <hfm/HFM.h>
|
||||||
|
|
||||||
|
#include "Engine.h"
|
||||||
|
|
||||||
|
#include <material-networking/MaterialCache.h>
|
||||||
|
|
||||||
|
class ParseMaterialMappingTask {
|
||||||
|
public:
|
||||||
|
using Input = QVariantHash;
|
||||||
|
using Output = MaterialMapping;
|
||||||
|
using JobModel = baker::Job::ModelIO<ParseMaterialMappingTask, Input, Output>;
|
||||||
|
|
||||||
|
void run(const baker::BakeContextPointer& context, const Input& input, Output& output);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_ParseMaterialMappingTask_h
|
|
@ -1,6 +1,8 @@
|
||||||
set(TARGET_NAME model-networking)
|
set(TARGET_NAME model-networking)
|
||||||
setup_hifi_library()
|
setup_hifi_library()
|
||||||
link_hifi_libraries(shared shaders networking graphics fbx ktx image gl model-baker)
|
link_hifi_libraries(shared shaders networking graphics fbx material-networking model-baker)
|
||||||
include_hifi_library_headers(gpu)
|
|
||||||
include_hifi_library_headers(hfm)
|
include_hifi_library_headers(hfm)
|
||||||
include_hifi_library_headers(task)
|
include_hifi_library_headers(task)
|
||||||
|
include_hifi_library_headers(gpu)
|
||||||
|
include_hifi_library_headers(image)
|
||||||
|
include_hifi_library_headers(ktx)
|
|
@ -1,60 +0,0 @@
|
||||||
//
|
|
||||||
// Created by Sam Gondelman on 2/9/2018
|
|
||||||
// Copyright 2018 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
#ifndef hifi_MaterialCache_h
|
|
||||||
#define hifi_MaterialCache_h
|
|
||||||
|
|
||||||
#include <ResourceCache.h>
|
|
||||||
|
|
||||||
#include "glm/glm.hpp"
|
|
||||||
|
|
||||||
#include "ModelCache.h"
|
|
||||||
|
|
||||||
class NetworkMaterialResource : public Resource {
|
|
||||||
public:
|
|
||||||
NetworkMaterialResource(const QUrl& url);
|
|
||||||
|
|
||||||
QString getType() const override { return "NetworkMaterial"; }
|
|
||||||
|
|
||||||
virtual void downloadFinished(const QByteArray& data) override;
|
|
||||||
|
|
||||||
typedef struct ParsedMaterials {
|
|
||||||
uint version { 1 };
|
|
||||||
std::vector<std::string> names;
|
|
||||||
std::unordered_map<std::string, std::shared_ptr<NetworkMaterial>> networkMaterials;
|
|
||||||
|
|
||||||
void reset() {
|
|
||||||
version = 1;
|
|
||||||
names.clear();
|
|
||||||
networkMaterials.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
} ParsedMaterials;
|
|
||||||
|
|
||||||
ParsedMaterials parsedMaterials;
|
|
||||||
|
|
||||||
static ParsedMaterials parseJSONMaterials(const QJsonDocument& materialJSON, const QUrl& baseUrl);
|
|
||||||
static std::pair<std::string, std::shared_ptr<NetworkMaterial>> parseJSONMaterial(const QJsonObject& materialJSON, const QUrl& baseUrl);
|
|
||||||
|
|
||||||
private:
|
|
||||||
static bool parseJSONColor(const QJsonValue& array, glm::vec3& color, bool& isSRGB);
|
|
||||||
};
|
|
||||||
|
|
||||||
using NetworkMaterialResourcePointer = QSharedPointer<NetworkMaterialResource>;
|
|
||||||
|
|
||||||
class MaterialCache : public ResourceCache {
|
|
||||||
public:
|
|
||||||
static MaterialCache& instance();
|
|
||||||
|
|
||||||
NetworkMaterialResourcePointer getMaterial(const QUrl& url);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual QSharedPointer<Resource> createResource(const QUrl& url) override;
|
|
||||||
QSharedPointer<Resource> createResourceCopy(const QSharedPointer<Resource>& resource) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -76,9 +76,9 @@ namespace std {
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct hash<GeometryExtra> {
|
struct hash<GeometryExtra> {
|
||||||
size_t operator()(const GeometryExtra& a) const {
|
size_t operator()(const GeometryExtra& geometryExtra) const {
|
||||||
size_t result = 0;
|
size_t result = 0;
|
||||||
hash_combine(result, a.mapping, a.textureBaseUrl, a.combineParts);
|
hash_combine(result, geometryExtra.mapping, geometryExtra.textureBaseUrl, geometryExtra.combineParts);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -174,6 +174,7 @@ void GeometryMappingResource::downloadFinished(const QByteArray& data) {
|
||||||
void GeometryMappingResource::onGeometryMappingLoaded(bool success) {
|
void GeometryMappingResource::onGeometryMappingLoaded(bool success) {
|
||||||
if (success && _geometryResource) {
|
if (success && _geometryResource) {
|
||||||
_hfmModel = _geometryResource->_hfmModel;
|
_hfmModel = _geometryResource->_hfmModel;
|
||||||
|
_materialMapping = _geometryResource->_materialMapping;
|
||||||
_meshParts = _geometryResource->_meshParts;
|
_meshParts = _geometryResource->_meshParts;
|
||||||
_meshes = _geometryResource->_meshes;
|
_meshes = _geometryResource->_meshes;
|
||||||
_materials = _geometryResource->_materials;
|
_materials = _geometryResource->_materials;
|
||||||
|
@ -341,6 +342,7 @@ void GeometryDefinitionResource::setGeometryDefinition(HFMModel::Pointer hfmMode
|
||||||
|
|
||||||
// Assume ownership of the processed HFMModel
|
// Assume ownership of the processed HFMModel
|
||||||
_hfmModel = modelBaker.hfmModel;
|
_hfmModel = modelBaker.hfmModel;
|
||||||
|
_materialMapping = modelBaker.materialMapping;
|
||||||
|
|
||||||
// Copy materials
|
// Copy materials
|
||||||
QHash<QString, size_t> materialIDAtlas;
|
QHash<QString, size_t> materialIDAtlas;
|
||||||
|
@ -392,7 +394,7 @@ QSharedPointer<Resource> ModelCache::createResource(const QUrl& url) {
|
||||||
}
|
}
|
||||||
|
|
||||||
QSharedPointer<Resource> ModelCache::createResourceCopy(const QSharedPointer<Resource>& resource) {
|
QSharedPointer<Resource> ModelCache::createResourceCopy(const QSharedPointer<Resource>& resource) {
|
||||||
return QSharedPointer<Resource>(new GeometryDefinitionResource(*resource.staticCast<GeometryDefinitionResource>().data()), &Resource::deleter);
|
return QSharedPointer<Resource>(new GeometryDefinitionResource(*resource.staticCast<GeometryDefinitionResource>()), &Resource::deleter);
|
||||||
}
|
}
|
||||||
|
|
||||||
GeometryResource::Pointer ModelCache::getGeometryResource(const QUrl& url,
|
GeometryResource::Pointer ModelCache::getGeometryResource(const QUrl& url,
|
||||||
|
@ -437,6 +439,7 @@ const QVariantMap Geometry::getTextures() const {
|
||||||
// FIXME: The materials should only be copied when modified, but the Model currently caches the original
|
// FIXME: The materials should only be copied when modified, but the Model currently caches the original
|
||||||
Geometry::Geometry(const Geometry& geometry) {
|
Geometry::Geometry(const Geometry& geometry) {
|
||||||
_hfmModel = geometry._hfmModel;
|
_hfmModel = geometry._hfmModel;
|
||||||
|
_materialMapping = geometry._materialMapping;
|
||||||
_meshes = geometry._meshes;
|
_meshes = geometry._meshes;
|
||||||
_meshParts = geometry._meshParts;
|
_meshParts = geometry._meshParts;
|
||||||
|
|
||||||
|
@ -556,310 +559,4 @@ void GeometryResourceWatcher::resourceRefreshed() {
|
||||||
// _instance.reset();
|
// _instance.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkMaterial::NetworkMaterial(const NetworkMaterial& m) :
|
|
||||||
Material(m),
|
|
||||||
_textures(m._textures),
|
|
||||||
_albedoTransform(m._albedoTransform),
|
|
||||||
_lightmapTransform(m._lightmapTransform),
|
|
||||||
_lightmapParams(m._lightmapParams),
|
|
||||||
_isOriginal(m._isOriginal)
|
|
||||||
{}
|
|
||||||
|
|
||||||
const QString NetworkMaterial::NO_TEXTURE = QString();
|
|
||||||
|
|
||||||
const QString& NetworkMaterial::getTextureName(MapChannel channel) {
|
|
||||||
if (_textures[channel].texture) {
|
|
||||||
return _textures[channel].name;
|
|
||||||
}
|
|
||||||
return NO_TEXTURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
QUrl NetworkMaterial::getTextureUrl(const QUrl& baseUrl, const HFMTexture& texture) {
|
|
||||||
if (texture.content.isEmpty()) {
|
|
||||||
// External file: search relative to the baseUrl, in case filename is relative
|
|
||||||
return baseUrl.resolved(QUrl(texture.filename));
|
|
||||||
} else {
|
|
||||||
// Inlined file: cache under the fbx file to avoid namespace clashes
|
|
||||||
// NOTE: We cannot resolve the path because filename may be an absolute path
|
|
||||||
assert(texture.filename.size() > 0);
|
|
||||||
auto baseUrlStripped = baseUrl.toDisplayString(QUrl::RemoveFragment | QUrl::RemoveQuery | QUrl::RemoveUserInfo);
|
|
||||||
if (texture.filename.at(0) == '/') {
|
|
||||||
return baseUrlStripped + texture.filename;
|
|
||||||
} else {
|
|
||||||
return baseUrlStripped + '/' + texture.filename;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
graphics::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& baseUrl, const HFMTexture& hfmTexture,
|
|
||||||
image::TextureUsage::Type type, MapChannel channel) {
|
|
||||||
|
|
||||||
if (baseUrl.isEmpty()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto url = getTextureUrl(baseUrl, hfmTexture);
|
|
||||||
const auto texture = DependencyManager::get<TextureCache>()->getTexture(url, type, hfmTexture.content, hfmTexture.maxNumPixels, hfmTexture.sourceChannel);
|
|
||||||
_textures[channel] = Texture { hfmTexture.name, texture };
|
|
||||||
|
|
||||||
auto map = std::make_shared<graphics::TextureMap>();
|
|
||||||
if (texture) {
|
|
||||||
map->setTextureSource(texture->_textureSource);
|
|
||||||
}
|
|
||||||
map->setTextureTransform(hfmTexture.transform);
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
graphics::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& url, image::TextureUsage::Type type, MapChannel channel) {
|
|
||||||
auto textureCache = DependencyManager::get<TextureCache>();
|
|
||||||
if (textureCache && !url.isEmpty()) {
|
|
||||||
auto texture = textureCache->getTexture(url, type);
|
|
||||||
_textures[channel].texture = texture;
|
|
||||||
|
|
||||||
auto map = std::make_shared<graphics::TextureMap>();
|
|
||||||
if (texture) {
|
|
||||||
map->setTextureSource(texture->_textureSource);
|
|
||||||
}
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NetworkMaterial::setAlbedoMap(const QUrl& url, bool useAlphaChannel) {
|
|
||||||
auto map = fetchTextureMap(url, image::TextureUsage::ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP);
|
|
||||||
if (map) {
|
|
||||||
map->setUseAlphaChannel(useAlphaChannel);
|
|
||||||
setTextureMap(MapChannel::ALBEDO_MAP, map);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NetworkMaterial::setNormalMap(const QUrl& url, bool isBumpmap) {
|
|
||||||
auto map = fetchTextureMap(url, isBumpmap ? image::TextureUsage::BUMP_TEXTURE : image::TextureUsage::NORMAL_TEXTURE, MapChannel::NORMAL_MAP);
|
|
||||||
if (map) {
|
|
||||||
setTextureMap(MapChannel::NORMAL_MAP, map);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NetworkMaterial::setRoughnessMap(const QUrl& url, bool isGloss) {
|
|
||||||
auto map = fetchTextureMap(url, isGloss ? image::TextureUsage::GLOSS_TEXTURE : image::TextureUsage::ROUGHNESS_TEXTURE, MapChannel::ROUGHNESS_MAP);
|
|
||||||
if (map) {
|
|
||||||
setTextureMap(MapChannel::ROUGHNESS_MAP, map);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NetworkMaterial::setMetallicMap(const QUrl& url, bool isSpecular) {
|
|
||||||
auto map = fetchTextureMap(url, isSpecular ? image::TextureUsage::SPECULAR_TEXTURE : image::TextureUsage::METALLIC_TEXTURE, MapChannel::METALLIC_MAP);
|
|
||||||
if (map) {
|
|
||||||
setTextureMap(MapChannel::METALLIC_MAP, map);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NetworkMaterial::setOcclusionMap(const QUrl& url) {
|
|
||||||
auto map = fetchTextureMap(url, image::TextureUsage::OCCLUSION_TEXTURE, MapChannel::OCCLUSION_MAP);
|
|
||||||
if (map) {
|
|
||||||
setTextureMap(MapChannel::OCCLUSION_MAP, map);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NetworkMaterial::setEmissiveMap(const QUrl& url) {
|
|
||||||
auto map = fetchTextureMap(url, image::TextureUsage::EMISSIVE_TEXTURE, MapChannel::EMISSIVE_MAP);
|
|
||||||
if (map) {
|
|
||||||
setTextureMap(MapChannel::EMISSIVE_MAP, map);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NetworkMaterial::setScatteringMap(const QUrl& url) {
|
|
||||||
auto map = fetchTextureMap(url, image::TextureUsage::SCATTERING_TEXTURE, MapChannel::SCATTERING_MAP);
|
|
||||||
if (map) {
|
|
||||||
setTextureMap(MapChannel::SCATTERING_MAP, map);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NetworkMaterial::setLightmapMap(const QUrl& url) {
|
|
||||||
auto map = fetchTextureMap(url, image::TextureUsage::LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP);
|
|
||||||
if (map) {
|
|
||||||
//map->setTextureTransform(_lightmapTransform);
|
|
||||||
//map->setLightmapOffsetScale(_lightmapParams.x, _lightmapParams.y);
|
|
||||||
setTextureMap(MapChannel::LIGHTMAP_MAP, map);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NetworkMaterial::NetworkMaterial(const HFMMaterial& material, const QUrl& textureBaseUrl) :
|
|
||||||
graphics::Material(*material._material),
|
|
||||||
_textures(MapChannel::NUM_MAP_CHANNELS)
|
|
||||||
{
|
|
||||||
_name = material.name.toStdString();
|
|
||||||
if (!material.albedoTexture.filename.isEmpty()) {
|
|
||||||
auto map = fetchTextureMap(textureBaseUrl, material.albedoTexture, image::TextureUsage::ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP);
|
|
||||||
if (map) {
|
|
||||||
_albedoTransform = material.albedoTexture.transform;
|
|
||||||
map->setTextureTransform(_albedoTransform);
|
|
||||||
|
|
||||||
if (!material.opacityTexture.filename.isEmpty()) {
|
|
||||||
if (material.albedoTexture.filename == material.opacityTexture.filename) {
|
|
||||||
// Best case scenario, just indicating that the albedo map contains transparency
|
|
||||||
// TODO: Different albedo/opacity maps are not currently supported
|
|
||||||
map->setUseAlphaChannel(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setTextureMap(MapChannel::ALBEDO_MAP, map);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (!material.normalTexture.filename.isEmpty()) {
|
|
||||||
auto type = (material.normalTexture.isBumpmap ? image::TextureUsage::BUMP_TEXTURE : image::TextureUsage::NORMAL_TEXTURE);
|
|
||||||
auto map = fetchTextureMap(textureBaseUrl, material.normalTexture, type, MapChannel::NORMAL_MAP);
|
|
||||||
setTextureMap(MapChannel::NORMAL_MAP, map);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!material.roughnessTexture.filename.isEmpty()) {
|
|
||||||
auto map = fetchTextureMap(textureBaseUrl, material.roughnessTexture, image::TextureUsage::ROUGHNESS_TEXTURE, MapChannel::ROUGHNESS_MAP);
|
|
||||||
setTextureMap(MapChannel::ROUGHNESS_MAP, map);
|
|
||||||
} else if (!material.glossTexture.filename.isEmpty()) {
|
|
||||||
auto map = fetchTextureMap(textureBaseUrl, material.glossTexture, image::TextureUsage::GLOSS_TEXTURE, MapChannel::ROUGHNESS_MAP);
|
|
||||||
setTextureMap(MapChannel::ROUGHNESS_MAP, map);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!material.metallicTexture.filename.isEmpty()) {
|
|
||||||
auto map = fetchTextureMap(textureBaseUrl, material.metallicTexture, image::TextureUsage::METALLIC_TEXTURE, MapChannel::METALLIC_MAP);
|
|
||||||
setTextureMap(MapChannel::METALLIC_MAP, map);
|
|
||||||
} else if (!material.specularTexture.filename.isEmpty()) {
|
|
||||||
auto map = fetchTextureMap(textureBaseUrl, material.specularTexture, image::TextureUsage::SPECULAR_TEXTURE, MapChannel::METALLIC_MAP);
|
|
||||||
setTextureMap(MapChannel::METALLIC_MAP, map);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!material.occlusionTexture.filename.isEmpty()) {
|
|
||||||
auto map = fetchTextureMap(textureBaseUrl, material.occlusionTexture, image::TextureUsage::OCCLUSION_TEXTURE, MapChannel::OCCLUSION_MAP);
|
|
||||||
if (map) {
|
|
||||||
map->setTextureTransform(material.occlusionTexture.transform);
|
|
||||||
}
|
|
||||||
setTextureMap(MapChannel::OCCLUSION_MAP, map);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!material.emissiveTexture.filename.isEmpty()) {
|
|
||||||
auto map = fetchTextureMap(textureBaseUrl, material.emissiveTexture, image::TextureUsage::EMISSIVE_TEXTURE, MapChannel::EMISSIVE_MAP);
|
|
||||||
setTextureMap(MapChannel::EMISSIVE_MAP, map);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!material.scatteringTexture.filename.isEmpty()) {
|
|
||||||
auto map = fetchTextureMap(textureBaseUrl, material.scatteringTexture, image::TextureUsage::SCATTERING_TEXTURE, MapChannel::SCATTERING_MAP);
|
|
||||||
setTextureMap(MapChannel::SCATTERING_MAP, map);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!material.lightmapTexture.filename.isEmpty()) {
|
|
||||||
auto map = fetchTextureMap(textureBaseUrl, material.lightmapTexture, image::TextureUsage::LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP);
|
|
||||||
if (map) {
|
|
||||||
_lightmapTransform = material.lightmapTexture.transform;
|
|
||||||
_lightmapParams = material.lightmapParams;
|
|
||||||
map->setTextureTransform(_lightmapTransform);
|
|
||||||
map->setLightmapOffsetScale(_lightmapParams.x, _lightmapParams.y);
|
|
||||||
}
|
|
||||||
setTextureMap(MapChannel::LIGHTMAP_MAP, map);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NetworkMaterial::setTextures(const QVariantMap& textureMap) {
|
|
||||||
_isOriginal = false;
|
|
||||||
|
|
||||||
const auto& albedoName = getTextureName(MapChannel::ALBEDO_MAP);
|
|
||||||
const auto& normalName = getTextureName(MapChannel::NORMAL_MAP);
|
|
||||||
const auto& roughnessName = getTextureName(MapChannel::ROUGHNESS_MAP);
|
|
||||||
const auto& metallicName = getTextureName(MapChannel::METALLIC_MAP);
|
|
||||||
const auto& occlusionName = getTextureName(MapChannel::OCCLUSION_MAP);
|
|
||||||
const auto& emissiveName = getTextureName(MapChannel::EMISSIVE_MAP);
|
|
||||||
const auto& lightmapName = getTextureName(MapChannel::LIGHTMAP_MAP);
|
|
||||||
const auto& scatteringName = getTextureName(MapChannel::SCATTERING_MAP);
|
|
||||||
|
|
||||||
if (!albedoName.isEmpty()) {
|
|
||||||
auto url = textureMap.contains(albedoName) ? textureMap[albedoName].toUrl() : QUrl();
|
|
||||||
auto map = fetchTextureMap(url, image::TextureUsage::ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP);
|
|
||||||
if (map) {
|
|
||||||
map->setTextureTransform(_albedoTransform);
|
|
||||||
// when reassigning the albedo texture we also check for the alpha channel used as opacity
|
|
||||||
map->setUseAlphaChannel(true);
|
|
||||||
}
|
|
||||||
setTextureMap(MapChannel::ALBEDO_MAP, map);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!normalName.isEmpty()) {
|
|
||||||
auto url = textureMap.contains(normalName) ? textureMap[normalName].toUrl() : QUrl();
|
|
||||||
auto map = fetchTextureMap(url, image::TextureUsage::NORMAL_TEXTURE, MapChannel::NORMAL_MAP);
|
|
||||||
setTextureMap(MapChannel::NORMAL_MAP, map);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!roughnessName.isEmpty()) {
|
|
||||||
auto url = textureMap.contains(roughnessName) ? textureMap[roughnessName].toUrl() : QUrl();
|
|
||||||
// FIXME: If passing a gloss map instead of a roughmap how do we know?
|
|
||||||
auto map = fetchTextureMap(url, image::TextureUsage::ROUGHNESS_TEXTURE, MapChannel::ROUGHNESS_MAP);
|
|
||||||
setTextureMap(MapChannel::ROUGHNESS_MAP, map);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!metallicName.isEmpty()) {
|
|
||||||
auto url = textureMap.contains(metallicName) ? textureMap[metallicName].toUrl() : QUrl();
|
|
||||||
// FIXME: If passing a specular map instead of a metallic how do we know?
|
|
||||||
auto map = fetchTextureMap(url, image::TextureUsage::METALLIC_TEXTURE, MapChannel::METALLIC_MAP);
|
|
||||||
setTextureMap(MapChannel::METALLIC_MAP, map);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!occlusionName.isEmpty()) {
|
|
||||||
auto url = textureMap.contains(occlusionName) ? textureMap[occlusionName].toUrl() : QUrl();
|
|
||||||
// FIXME: we need to handle the occlusion map transform here
|
|
||||||
auto map = fetchTextureMap(url, image::TextureUsage::OCCLUSION_TEXTURE, MapChannel::OCCLUSION_MAP);
|
|
||||||
setTextureMap(MapChannel::OCCLUSION_MAP, map);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!emissiveName.isEmpty()) {
|
|
||||||
auto url = textureMap.contains(emissiveName) ? textureMap[emissiveName].toUrl() : QUrl();
|
|
||||||
auto map = fetchTextureMap(url, image::TextureUsage::EMISSIVE_TEXTURE, MapChannel::EMISSIVE_MAP);
|
|
||||||
setTextureMap(MapChannel::EMISSIVE_MAP, map);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!scatteringName.isEmpty()) {
|
|
||||||
auto url = textureMap.contains(scatteringName) ? textureMap[scatteringName].toUrl() : QUrl();
|
|
||||||
auto map = fetchTextureMap(url, image::TextureUsage::SCATTERING_TEXTURE, MapChannel::SCATTERING_MAP);
|
|
||||||
setTextureMap(MapChannel::SCATTERING_MAP, map);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!lightmapName.isEmpty()) {
|
|
||||||
auto url = textureMap.contains(lightmapName) ? textureMap[lightmapName].toUrl() : QUrl();
|
|
||||||
auto map = fetchTextureMap(url, image::TextureUsage::LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP);
|
|
||||||
if (map) {
|
|
||||||
map->setTextureTransform(_lightmapTransform);
|
|
||||||
map->setLightmapOffsetScale(_lightmapParams.x, _lightmapParams.y);
|
|
||||||
}
|
|
||||||
setTextureMap(MapChannel::LIGHTMAP_MAP, map);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NetworkMaterial::isMissingTexture() {
|
|
||||||
for (auto& networkTexture : _textures) {
|
|
||||||
auto& texture = networkTexture.texture;
|
|
||||||
if (!texture) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Failed texture downloads need to be considered as 'loaded'
|
|
||||||
// or the object will never fade in
|
|
||||||
bool finished = texture->isFailed() || (texture->isLoaded() && texture->getGPUTexture() && texture->getGPUTexture()->isDefined());
|
|
||||||
if (!finished) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NetworkMaterial::checkResetOpacityMap() {
|
|
||||||
// If material textures are loaded, check the material translucency
|
|
||||||
// FIXME: This should not be done here. The opacity map should already be reset in Material::setTextureMap.
|
|
||||||
// However, currently that code can be called before the albedo map is defined, so resetOpacityMap will fail.
|
|
||||||
// Geometry::areTexturesLoaded() is called repeatedly until it returns true, so we do the check here for now
|
|
||||||
const auto& albedoTexture = _textures[NetworkMaterial::MapChannel::ALBEDO_MAP];
|
|
||||||
if (albedoTexture.texture) {
|
|
||||||
resetOpacityMap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "ModelCache.moc"
|
#include "ModelCache.moc"
|
||||||
|
|
|
@ -15,17 +15,13 @@
|
||||||
#include <DependencyManager.h>
|
#include <DependencyManager.h>
|
||||||
#include <ResourceCache.h>
|
#include <ResourceCache.h>
|
||||||
|
|
||||||
#include <graphics/Material.h>
|
|
||||||
#include <graphics/Asset.h>
|
#include <graphics/Asset.h>
|
||||||
|
|
||||||
#include "FBXSerializer.h"
|
#include "FBXSerializer.h"
|
||||||
#include "TextureCache.h"
|
#include <material-networking/MaterialCache.h>
|
||||||
|
#include <material-networking/TextureCache.h>
|
||||||
#include "ModelLoader.h"
|
#include "ModelLoader.h"
|
||||||
|
|
||||||
// Alias instead of derive to avoid copying
|
|
||||||
|
|
||||||
class NetworkTexture;
|
|
||||||
class NetworkMaterial;
|
|
||||||
class MeshPart;
|
class MeshPart;
|
||||||
|
|
||||||
class GeometryMappingResource;
|
class GeometryMappingResource;
|
||||||
|
@ -49,6 +45,7 @@ public:
|
||||||
bool isHFMModelLoaded() const { return (bool)_hfmModel; }
|
bool isHFMModelLoaded() const { return (bool)_hfmModel; }
|
||||||
|
|
||||||
const HFMModel& getHFMModel() const { return *_hfmModel; }
|
const HFMModel& getHFMModel() const { return *_hfmModel; }
|
||||||
|
const MaterialMapping& getMaterialMapping() const { return _materialMapping; }
|
||||||
const GeometryMeshes& getMeshes() const { return *_meshes; }
|
const GeometryMeshes& getMeshes() const { return *_meshes; }
|
||||||
const std::shared_ptr<NetworkMaterial> getShapeMaterial(int shapeID) const;
|
const std::shared_ptr<NetworkMaterial> getShapeMaterial(int shapeID) const;
|
||||||
|
|
||||||
|
@ -64,6 +61,7 @@ protected:
|
||||||
|
|
||||||
// Shared across all geometries, constant throughout lifetime
|
// Shared across all geometries, constant throughout lifetime
|
||||||
std::shared_ptr<const HFMModel> _hfmModel;
|
std::shared_ptr<const HFMModel> _hfmModel;
|
||||||
|
MaterialMapping _materialMapping;
|
||||||
std::shared_ptr<const GeometryMeshes> _meshes;
|
std::shared_ptr<const GeometryMeshes> _meshes;
|
||||||
std::shared_ptr<const GeometryMeshParts> _meshParts;
|
std::shared_ptr<const GeometryMeshParts> _meshParts;
|
||||||
|
|
||||||
|
@ -166,59 +164,6 @@ private:
|
||||||
ModelLoader _modelLoader;
|
ModelLoader _modelLoader;
|
||||||
};
|
};
|
||||||
|
|
||||||
class NetworkMaterial : public graphics::Material {
|
|
||||||
public:
|
|
||||||
using MapChannel = graphics::Material::MapChannel;
|
|
||||||
|
|
||||||
NetworkMaterial() : _textures(MapChannel::NUM_MAP_CHANNELS) {}
|
|
||||||
NetworkMaterial(const HFMMaterial& material, const QUrl& textureBaseUrl);
|
|
||||||
NetworkMaterial(const NetworkMaterial& material);
|
|
||||||
|
|
||||||
void setAlbedoMap(const QUrl& url, bool useAlphaChannel);
|
|
||||||
void setNormalMap(const QUrl& url, bool isBumpmap);
|
|
||||||
void setRoughnessMap(const QUrl& url, bool isGloss);
|
|
||||||
void setMetallicMap(const QUrl& url, bool isSpecular);
|
|
||||||
void setOcclusionMap(const QUrl& url);
|
|
||||||
void setEmissiveMap(const QUrl& url);
|
|
||||||
void setScatteringMap(const QUrl& url);
|
|
||||||
void setLightmapMap(const QUrl& url);
|
|
||||||
|
|
||||||
bool isMissingTexture();
|
|
||||||
void checkResetOpacityMap();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
friend class Geometry;
|
|
||||||
|
|
||||||
class Texture {
|
|
||||||
public:
|
|
||||||
QString name;
|
|
||||||
NetworkTexturePointer texture;
|
|
||||||
};
|
|
||||||
using Textures = std::vector<Texture>;
|
|
||||||
|
|
||||||
Textures _textures;
|
|
||||||
|
|
||||||
static const QString NO_TEXTURE;
|
|
||||||
const QString& getTextureName(MapChannel channel);
|
|
||||||
|
|
||||||
void setTextures(const QVariantMap& textureMap);
|
|
||||||
|
|
||||||
const bool& isOriginal() const { return _isOriginal; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Helpers for the ctors
|
|
||||||
QUrl getTextureUrl(const QUrl& baseUrl, const HFMTexture& hfmTexture);
|
|
||||||
graphics::TextureMapPointer fetchTextureMap(const QUrl& baseUrl, const HFMTexture& hfmTexture,
|
|
||||||
image::TextureUsage::Type type, MapChannel channel);
|
|
||||||
graphics::TextureMapPointer fetchTextureMap(const QUrl& url, image::TextureUsage::Type type, MapChannel channel);
|
|
||||||
|
|
||||||
Transform _albedoTransform;
|
|
||||||
Transform _lightmapTransform;
|
|
||||||
vec2 _lightmapParams;
|
|
||||||
|
|
||||||
bool _isOriginal { true };
|
|
||||||
};
|
|
||||||
|
|
||||||
class MeshPart {
|
class MeshPart {
|
||||||
public:
|
public:
|
||||||
MeshPart(int mesh, int part, int material) : meshID { mesh }, partID { part }, materialID { material } {}
|
MeshPart(int mesh, int part, int material) : meshID { mesh }, partID { part }, materialID { material } {}
|
||||||
|
|
|
@ -8,4 +8,4 @@
|
||||||
|
|
||||||
#include "ModelNetworkingLogging.h"
|
#include "ModelNetworkingLogging.h"
|
||||||
|
|
||||||
Q_LOGGING_CATEGORY(modelnetworking, "hifi.gpu-network")
|
Q_LOGGING_CATEGORY(modelnetworking, "hifi.gpu-model-network")
|
||||||
|
|
|
@ -360,7 +360,7 @@ class Resource : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
Resource() : QObject(), _loaded(true) {}
|
||||||
Resource(const Resource& other);
|
Resource(const Resource& other);
|
||||||
Resource(const QUrl& url);
|
Resource(const QUrl& url);
|
||||||
virtual ~Resource();
|
virtual ~Resource();
|
||||||
|
@ -491,14 +491,14 @@ protected:
|
||||||
QWeakPointer<Resource> _self;
|
QWeakPointer<Resource> _self;
|
||||||
QPointer<ResourceCache> _cache;
|
QPointer<ResourceCache> _cache;
|
||||||
|
|
||||||
qint64 _bytesReceived{ 0 };
|
qint64 _bytesReceived { 0 };
|
||||||
qint64 _bytesTotal{ 0 };
|
qint64 _bytesTotal { 0 };
|
||||||
qint64 _bytes{ 0 };
|
qint64 _bytes { 0 };
|
||||||
|
|
||||||
int _requestID;
|
int _requestID;
|
||||||
ResourceRequest* _request{ nullptr };
|
ResourceRequest* _request { nullptr };
|
||||||
|
|
||||||
size_t _extraHash;
|
size_t _extraHash { std::numeric_limits<size_t>::max() };
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void handleDownloadProgress(uint64_t bytesReceived, uint64_t bytesTotal);
|
void handleDownloadProgress(uint64_t bytesReceived, uint64_t bytesTotal);
|
||||||
|
|
|
@ -7,6 +7,7 @@ include_hifi_library_headers(avatars)
|
||||||
include_hifi_library_headers(audio)
|
include_hifi_library_headers(audio)
|
||||||
include_hifi_library_headers(octree)
|
include_hifi_library_headers(octree)
|
||||||
include_hifi_library_headers(animation)
|
include_hifi_library_headers(animation)
|
||||||
|
include_hifi_library_headers(material-networking)
|
||||||
include_hifi_library_headers(model-networking)
|
include_hifi_library_headers(model-networking)
|
||||||
include_hifi_library_headers(image)
|
include_hifi_library_headers(image)
|
||||||
include_hifi_library_headers(ktx)
|
include_hifi_library_headers(ktx)
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
set(TARGET_NAME procedural)
|
set(TARGET_NAME procedural)
|
||||||
setup_hifi_library()
|
setup_hifi_library()
|
||||||
link_hifi_libraries(shared gpu shaders networking graphics model-networking ktx image)
|
|
||||||
|
link_hifi_libraries(shared gpu shaders networking graphics material-networking ktx image)
|
||||||
|
|
|
@ -19,8 +19,8 @@
|
||||||
#include <gpu/Shader.h>
|
#include <gpu/Shader.h>
|
||||||
#include <gpu/Pipeline.h>
|
#include <gpu/Pipeline.h>
|
||||||
#include <gpu/Batch.h>
|
#include <gpu/Batch.h>
|
||||||
#include <model-networking/ShaderCache.h>
|
#include <material-networking/ShaderCache.h>
|
||||||
#include <model-networking/TextureCache.h>
|
#include <material-networking/TextureCache.h>
|
||||||
|
|
||||||
using UniformLambdas = std::list<std::function<void(gpu::Batch& batch)>>;
|
using UniformLambdas = std::list<std::function<void(gpu::Batch& batch)>>;
|
||||||
const size_t MAX_PROCEDURAL_TEXTURE_CHANNELS{ 4 };
|
const size_t MAX_PROCEDURAL_TEXTURE_CHANNELS{ 4 };
|
||||||
|
|
|
@ -54,5 +54,5 @@ QSharedPointer<Resource> ClipCache::createResource(const QUrl& url) {
|
||||||
}
|
}
|
||||||
|
|
||||||
QSharedPointer<Resource> ClipCache::createResourceCopy(const QSharedPointer<Resource>& resource) {
|
QSharedPointer<Resource> ClipCache::createResourceCopy(const QSharedPointer<Resource>& resource) {
|
||||||
return QSharedPointer<Resource>(new NetworkClipLoader(*resource.staticCast<NetworkClipLoader>().data()), &Resource::deleter);
|
return QSharedPointer<Resource>(new NetworkClipLoader(*resource.staticCast<NetworkClipLoader>()), &Resource::deleter);
|
||||||
}
|
}
|
|
@ -3,7 +3,7 @@ set(TARGET_NAME render-utils)
|
||||||
# pull in the resources.qrc file
|
# pull in the resources.qrc file
|
||||||
qt5_add_resources(QT_RESOURCES_FILE "${CMAKE_CURRENT_SOURCE_DIR}/res/fonts/fonts.qrc")
|
qt5_add_resources(QT_RESOURCES_FILE "${CMAKE_CURRENT_SOURCE_DIR}/res/fonts/fonts.qrc")
|
||||||
setup_hifi_library(Gui Network Qml Quick Script)
|
setup_hifi_library(Gui Network Qml Quick Script)
|
||||||
link_hifi_libraries(shared task ktx gpu shaders graphics graphics-scripting model-networking render animation fbx image procedural)
|
link_hifi_libraries(shared task ktx gpu shaders graphics graphics-scripting material-networking model-networking render animation fbx image procedural)
|
||||||
include_hifi_library_headers(audio)
|
include_hifi_library_headers(audio)
|
||||||
include_hifi_library_headers(networking)
|
include_hifi_library_headers(networking)
|
||||||
include_hifi_library_headers(octree)
|
include_hifi_library_headers(octree)
|
||||||
|
|
|
@ -756,7 +756,16 @@ scriptable::ScriptableModelBase Model::getScriptableModel() {
|
||||||
|
|
||||||
int numParts = (int)mesh->getNumParts();
|
int numParts = (int)mesh->getNumParts();
|
||||||
for (int partIndex = 0; partIndex < numParts; partIndex++) {
|
for (int partIndex = 0; partIndex < numParts; partIndex++) {
|
||||||
result.appendMaterial(graphics::MaterialLayer(getGeometry()->getShapeMaterial(shapeID), 0), shapeID, _modelMeshMaterialNames[shapeID]);
|
auto& materialName = _modelMeshMaterialNames[shapeID];
|
||||||
|
result.appendMaterial(graphics::MaterialLayer(getGeometry()->getShapeMaterial(shapeID), 0), shapeID, materialName);
|
||||||
|
|
||||||
|
auto mappedMaterialIter = _materialMapping.find(shapeID);
|
||||||
|
if (mappedMaterialIter != _materialMapping.end()) {
|
||||||
|
auto mappedMaterials = mappedMaterialIter->second;
|
||||||
|
for (auto& mappedMaterial : mappedMaterials) {
|
||||||
|
result.appendMaterial(mappedMaterial, shapeID, materialName);
|
||||||
|
}
|
||||||
|
}
|
||||||
shapeID++;
|
shapeID++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -956,6 +965,7 @@ bool Model::addToScene(const render::ScenePointer& scene,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (somethingAdded) {
|
if (somethingAdded) {
|
||||||
|
applyMaterialMapping();
|
||||||
_addedToScene = true;
|
_addedToScene = true;
|
||||||
updateRenderItems();
|
updateRenderItems();
|
||||||
_needsFixupInScene = false;
|
_needsFixupInScene = false;
|
||||||
|
@ -973,6 +983,7 @@ void Model::removeFromScene(const render::ScenePointer& scene, render::Transacti
|
||||||
_modelMeshRenderItems.clear();
|
_modelMeshRenderItems.clear();
|
||||||
_modelMeshMaterialNames.clear();
|
_modelMeshMaterialNames.clear();
|
||||||
_modelMeshRenderItemShapes.clear();
|
_modelMeshRenderItemShapes.clear();
|
||||||
|
_priorityMap.clear();
|
||||||
|
|
||||||
_blendshapeOffsets.clear();
|
_blendshapeOffsets.clear();
|
||||||
_blendshapeOffsetsInitialized = false;
|
_blendshapeOffsetsInitialized = false;
|
||||||
|
@ -1519,17 +1530,65 @@ std::set<unsigned int> Model::getMeshIDsFromMaterialID(QString parentMaterialNam
|
||||||
return toReturn;
|
return toReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Model::applyMaterialMapping() {
|
||||||
|
auto renderItemsKey = _renderItemKeyGlobalFlags;
|
||||||
|
PrimitiveMode primitiveMode = getPrimitiveMode();
|
||||||
|
bool useDualQuaternionSkinning = _useDualQuaternionSkinning;
|
||||||
|
|
||||||
|
auto& materialMapping = getMaterialMapping();
|
||||||
|
for (auto& mapping : materialMapping) {
|
||||||
|
std::set<unsigned int> shapeIDs = getMeshIDsFromMaterialID(QString(mapping.first.c_str()));
|
||||||
|
auto networkMaterialResource = mapping.second;
|
||||||
|
if (!networkMaterialResource || shapeIDs.size() == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto materialLoaded = [this, networkMaterialResource, shapeIDs, renderItemsKey, primitiveMode, useDualQuaternionSkinning]() {
|
||||||
|
if (networkMaterialResource->isFailed() || networkMaterialResource->parsedMaterials.names.size() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
render::Transaction transaction;
|
||||||
|
auto networkMaterial = networkMaterialResource->parsedMaterials.networkMaterials[networkMaterialResource->parsedMaterials.names[0]];
|
||||||
|
for (auto shapeID : shapeIDs) {
|
||||||
|
if (shapeID < _modelMeshRenderItemIDs.size()) {
|
||||||
|
auto itemID = _modelMeshRenderItemIDs[shapeID];
|
||||||
|
auto meshIndex = _modelMeshRenderItemShapes[shapeID].meshIndex;
|
||||||
|
bool invalidatePayloadShapeKey = shouldInvalidatePayloadShapeKey(meshIndex);
|
||||||
|
graphics::MaterialLayer material = graphics::MaterialLayer(networkMaterial, ++_priorityMap[shapeID]);
|
||||||
|
_materialMapping[shapeID].push_back(material);
|
||||||
|
transaction.updateItem<ModelMeshPartPayload>(itemID, [material, renderItemsKey,
|
||||||
|
invalidatePayloadShapeKey, primitiveMode, useDualQuaternionSkinning](ModelMeshPartPayload& data) {
|
||||||
|
data.addMaterial(material);
|
||||||
|
// if the material changed, we might need to update our item key or shape key
|
||||||
|
data.updateKey(renderItemsKey);
|
||||||
|
data.setShapeKey(invalidatePayloadShapeKey, primitiveMode, useDualQuaternionSkinning);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (networkMaterialResource->isLoaded()) {
|
||||||
|
materialLoaded();
|
||||||
|
} else {
|
||||||
|
connect(networkMaterialResource.data(), &Resource::finished, materialLoaded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Model::addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName) {
|
void Model::addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName) {
|
||||||
std::set<unsigned int> shapeIDs = getMeshIDsFromMaterialID(QString(parentMaterialName.c_str()));
|
std::set<unsigned int> shapeIDs = getMeshIDsFromMaterialID(QString(parentMaterialName.c_str()));
|
||||||
|
|
||||||
|
auto renderItemsKey = _renderItemKeyGlobalFlags;
|
||||||
|
PrimitiveMode primitiveMode = getPrimitiveMode();
|
||||||
|
bool useDualQuaternionSkinning = _useDualQuaternionSkinning;
|
||||||
|
|
||||||
render::Transaction transaction;
|
render::Transaction transaction;
|
||||||
for (auto shapeID : shapeIDs) {
|
for (auto shapeID : shapeIDs) {
|
||||||
if (shapeID < _modelMeshRenderItemIDs.size()) {
|
if (shapeID < _modelMeshRenderItemIDs.size()) {
|
||||||
auto itemID = _modelMeshRenderItemIDs[shapeID];
|
auto itemID = _modelMeshRenderItemIDs[shapeID];
|
||||||
auto renderItemsKey = _renderItemKeyGlobalFlags;
|
|
||||||
PrimitiveMode primitiveMode = getPrimitiveMode();
|
|
||||||
auto meshIndex = _modelMeshRenderItemShapes[shapeID].meshIndex;
|
auto meshIndex = _modelMeshRenderItemShapes[shapeID].meshIndex;
|
||||||
bool invalidatePayloadShapeKey = shouldInvalidatePayloadShapeKey(meshIndex);
|
bool invalidatePayloadShapeKey = shouldInvalidatePayloadShapeKey(meshIndex);
|
||||||
bool useDualQuaternionSkinning = _useDualQuaternionSkinning;
|
|
||||||
transaction.updateItem<ModelMeshPartPayload>(itemID, [material, renderItemsKey,
|
transaction.updateItem<ModelMeshPartPayload>(itemID, [material, renderItemsKey,
|
||||||
invalidatePayloadShapeKey, primitiveMode, useDualQuaternionSkinning](ModelMeshPartPayload& data) {
|
invalidatePayloadShapeKey, primitiveMode, useDualQuaternionSkinning](ModelMeshPartPayload& data) {
|
||||||
data.addMaterial(material);
|
data.addMaterial(material);
|
||||||
|
|
|
@ -183,6 +183,7 @@ public:
|
||||||
/// Provided as a convenience, will crash if !isLoaded()
|
/// Provided as a convenience, will crash if !isLoaded()
|
||||||
// And so that getHFMModel() isn't chained everywhere
|
// And so that getHFMModel() isn't chained everywhere
|
||||||
const HFMModel& getHFMModel() const { assert(isLoaded()); return _renderGeometry->getHFMModel(); }
|
const HFMModel& getHFMModel() const { assert(isLoaded()); return _renderGeometry->getHFMModel(); }
|
||||||
|
const MaterialMapping& getMaterialMapping() const { assert(isLoaded()); return _renderGeometry->getMaterialMapping(); }
|
||||||
|
|
||||||
bool isActive() const { return isLoaded(); }
|
bool isActive() const { return isLoaded(); }
|
||||||
|
|
||||||
|
@ -373,6 +374,10 @@ signals:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
std::unordered_map<unsigned int, quint16> _priorityMap; // only used for materialMapping
|
||||||
|
std::unordered_map<unsigned int, std::vector<graphics::MaterialLayer>> _materialMapping; // generated during applyMaterialMapping
|
||||||
|
void applyMaterialMapping();
|
||||||
|
|
||||||
void setBlendshapeCoefficients(const QVector<float>& coefficients) { _blendshapeCoefficients = coefficients; }
|
void setBlendshapeCoefficients(const QVector<float>& coefficients) { _blendshapeCoefficients = coefficients; }
|
||||||
const QVector<float>& getBlendshapeCoefficients() const { return _blendshapeCoefficients; }
|
const QVector<float>& getBlendshapeCoefficients() const { return _blendshapeCoefficients; }
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
#include <gpu/Context.h>
|
#include <gpu/Context.h>
|
||||||
#include <model-networking/TextureCache.h>
|
#include <material-networking/TextureCache.h>
|
||||||
#include <render/DrawTask.h>
|
#include <render/DrawTask.h>
|
||||||
#include <shaders/Shaders.h>
|
#include <shaders/Shaders.h>
|
||||||
#include <graphics/ShaderConstants.h>
|
#include <graphics/ShaderConstants.h>
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
// Compatibility
|
// Compatibility
|
||||||
#include <model-networking/TextureCache.h>
|
#include <material-networking/TextureCache.h>
|
||||||
|
|
|
@ -17,6 +17,6 @@ if (NOT ANDROID)
|
||||||
|
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
link_hifi_libraries(shared networking octree shaders gpu procedural graphics model-networking ktx recording avatars fbx hfm entities controllers animation audio physics image midi)
|
link_hifi_libraries(shared networking octree shaders gpu procedural graphics material-networking model-networking ktx recording avatars fbx hfm entities controllers animation audio physics image midi)
|
||||||
# ui includes gl, but link_hifi_libraries does not use transitive includes, so gl must be explicit
|
# ui includes gl, but link_hifi_libraries does not use transitive includes, so gl must be explicit
|
||||||
include_hifi_library_headers(gl)
|
include_hifi_library_headers(gl)
|
||||||
|
|
|
@ -11,7 +11,7 @@ if (WIN32 AND (NOT USE_GLES))
|
||||||
setup_hifi_plugin(Gui Qml Multimedia)
|
setup_hifi_plugin(Gui Qml Multimedia)
|
||||||
link_hifi_libraries(shared task gl qml networking controllers ui
|
link_hifi_libraries(shared task gl qml networking controllers ui
|
||||||
plugins display-plugins ui-plugins input-plugins script-engine
|
plugins display-plugins ui-plugins input-plugins script-engine
|
||||||
audio-client render-utils graphics shaders gpu render model-networking model-baker hfm fbx ktx image procedural ${PLATFORM_GL_BACKEND})
|
audio-client render-utils graphics shaders gpu render material-networking model-networking model-baker hfm fbx ktx image procedural ${PLATFORM_GL_BACKEND})
|
||||||
include_hifi_library_headers(octree)
|
include_hifi_library_headers(octree)
|
||||||
|
|
||||||
target_openvr()
|
target_openvr()
|
||||||
|
|
|
@ -285,6 +285,10 @@ SelectionManager = (function() {
|
||||||
properties.localPosition = properties.position;
|
properties.localPosition = properties.position;
|
||||||
properties.localRotation = properties.rotation;
|
properties.localRotation = properties.rotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
properties.localVelocity = Vec3.ZERO;
|
||||||
|
properties.localAngularVelocity = Vec3.ZERO;
|
||||||
|
|
||||||
delete properties.actionData;
|
delete properties.actionData;
|
||||||
var newEntityID = Entities.addEntity(properties);
|
var newEntityID = Entities.addEntity(properties);
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ endfunction()
|
||||||
|
|
||||||
if (BUILD_TOOLS)
|
if (BUILD_TOOLS)
|
||||||
# Allow different tools for stable builds
|
# Allow different tools for stable builds
|
||||||
if (STABLE_BUILD)
|
if (RELEASE_TYPE STREQUAL "PRODUCTION")
|
||||||
set(ALL_TOOLS
|
set(ALL_TOOLS
|
||||||
udt-test
|
udt-test
|
||||||
vhacd-util
|
vhacd-util
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
|
||||||
#include <quazip5/quazip.h>
|
#include <quazip5/quazip.h>
|
||||||
#include <quazip5/JlCompress.h>
|
#include <quazip5/JlCompress.h>
|
||||||
|
|
|
@ -40,7 +40,11 @@ Nitpick::Nitpick(QWidget* parent) : QMainWindow(parent) {
|
||||||
|
|
||||||
_ui.plainTextEdit->setReadOnly(true);
|
_ui.plainTextEdit->setReadOnly(true);
|
||||||
|
|
||||||
setWindowTitle("Nitpick - v2.1.2");
|
setWindowTitle("Nitpick - v3.0.0");
|
||||||
|
|
||||||
|
clientProfiles << "VR-High" << "Desktop-High" << "Desktop-Low" << "Mobile-Touch" << "VR-Standalone";
|
||||||
|
_ui.clientProfileComboBox->insertItems(0, clientProfiles);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Nitpick::~Nitpick() {
|
Nitpick::~Nitpick() {
|
||||||
|
@ -157,7 +161,7 @@ void Nitpick::on_createAllRecursiveScriptsPushbutton_clicked() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Nitpick::on_createTestsPushbutton_clicked() {
|
void Nitpick::on_createTestsPushbutton_clicked() {
|
||||||
_test->createTests();
|
_test->createTests(_ui.clientProfileComboBox->currentText());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Nitpick::on_createMDFilePushbutton_clicked() {
|
void Nitpick::on_createMDFilePushbutton_clicked() {
|
||||||
|
|
|
@ -126,6 +126,8 @@ private:
|
||||||
bool _isRunningFromCommandline{ false };
|
bool _isRunningFromCommandline{ false };
|
||||||
|
|
||||||
void* _caller;
|
void* _caller;
|
||||||
|
|
||||||
|
QStringList clientProfiles;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_Nitpick_h
|
#endif // hifi_Nitpick_h
|
|
@ -391,7 +391,7 @@ void Test::includeTest(QTextStream& textStream, const QString& testPathname) {
|
||||||
textStream << "Script.include(testsRootPath + \"" << partialPathWithoutTests + "\");" << endl;
|
textStream << "Script.include(testsRootPath + \"" << partialPathWithoutTests + "\");" << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Test::createTests() {
|
void Test::createTests(const QString& clientProfile) {
|
||||||
// Rename files sequentially, as ExpectedResult_00000.png, ExpectedResult_00001.png and so on
|
// Rename files sequentially, as ExpectedResult_00000.png, ExpectedResult_00001.png and so on
|
||||||
// Any existing expected result images will be deleted
|
// Any existing expected result images will be deleted
|
||||||
QString previousSelection = _snapshotDirectory;
|
QString previousSelection = _snapshotDirectory;
|
||||||
|
|
|
@ -52,7 +52,7 @@ public:
|
||||||
|
|
||||||
void finishTestsEvaluation();
|
void finishTestsEvaluation();
|
||||||
|
|
||||||
void createTests();
|
void createTests(const QString& clientProfile);
|
||||||
|
|
||||||
void createTestsOutline();
|
void createTestsOutline();
|
||||||
|
|
||||||
|
|
|
@ -52,8 +52,8 @@
|
||||||
<widget class="QPushButton" name="createTestsPushbutton">
|
<widget class="QPushButton" name="createTestsPushbutton">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>210</x>
|
<x>70</x>
|
||||||
<y>60</y>
|
<y>40</y>
|
||||||
<width>220</width>
|
<width>220</width>
|
||||||
<height>40</height>
|
<height>40</height>
|
||||||
</rect>
|
</rect>
|
||||||
|
@ -153,6 +153,16 @@
|
||||||
<string>Create all testAuto scripts</string>
|
<string>Create all testAuto scripts</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
|
<widget class="QComboBox" name="clientProfileComboBox">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>320</x>
|
||||||
|
<y>40</y>
|
||||||
|
<width>120</width>
|
||||||
|
<height>40</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="tab_4">
|
<widget class="QWidget" name="tab_4">
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
|
|
Loading…
Reference in a new issue