diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 05a937eef8..e3118c0048 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -113,7 +113,7 @@ endif() target_link_libraries(${TARGET_NAME} ${BULLET_LIBRARIES}) # link required hifi libraries -link_hifi_libraries(shared octree environment gpu gpu-networking procedural model render fbx networking entities avatars +link_hifi_libraries(shared octree environment gpu procedural model render fbx networking model-networking entities avatars audio audio-client animation script-engine physics render-utils entities-renderer ui auto-updater plugins display-plugins input-plugins) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1b6be53e83..798b1ab9a2 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -278,6 +278,7 @@ bool setupEssentials(int& argc, char** argv) { auto addressManager = DependencyManager::set(); auto nodeList = DependencyManager::set(NodeType::Agent, listenPort); auto geometryCache = DependencyManager::set(); + auto modelCache = DependencyManager::set(); auto scriptCache = DependencyManager::set(); auto soundCache = DependencyManager::set(); auto faceshift = DependencyManager::set(); @@ -418,12 +419,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : // put the NodeList and datagram processing on the node thread nodeList->moveToThread(nodeThread); - // geometry background downloads need to happen on the Datagram Processor Thread. The idle loop will - // emit checkBackgroundDownloads to cause the GeometryCache to check it's queue for requested background + // Model background downloads need to happen on the Datagram Processor Thread. The idle loop will + // emit checkBackgroundDownloads to cause the ModelCache to check it's queue for requested background // downloads. - QSharedPointer geometryCacheP = DependencyManager::get(); - ResourceCache* geometryCache = geometryCacheP.data(); - connect(this, &Application::checkBackgroundDownloads, geometryCache, &ResourceCache::checkAsynchronousGets); + QSharedPointer modelCacheP = DependencyManager::get(); + ResourceCache* modelCache = modelCacheP.data(); + connect(this, &Application::checkBackgroundDownloads, modelCache, &ResourceCache::checkAsynchronousGets); // put the audio processing on a separate thread QThread* audioThread = new QThread(); @@ -892,6 +893,7 @@ Application::~Application() { DependencyManager::destroy(); DependencyManager::destroy(); DependencyManager::destroy(); + DependencyManager::destroy(); DependencyManager::destroy(); DependencyManager::destroy(); DependencyManager::destroy(); @@ -2718,7 +2720,7 @@ void Application::reloadResourceCaches() { emptyLocalCache(); DependencyManager::get()->refreshAll(); - DependencyManager::get()->refreshAll(); + DependencyManager::get()->refreshAll(); DependencyManager::get()->refreshAll(); DependencyManager::get()->refreshAll(); } diff --git a/interface/src/Stars.cpp b/interface/src/Stars.cpp index 119b9ed1a2..e2b64869ec 100644 --- a/interface/src/Stars.cpp +++ b/interface/src/Stars.cpp @@ -188,8 +188,10 @@ void Stars::render(RenderArgs* renderArgs, float alpha) { colorElement = streamFormat->getAttributes().at(gpu::Stream::COLOR)._element; }); - auto geometryCache = DependencyManager::get(); + auto modelCache = DependencyManager::get(); auto textureCache = DependencyManager::get(); + auto geometryCache = DependencyManager::get(); + gpu::Batch& batch = *renderArgs->_batch; batch.setViewTransform(Transform()); diff --git a/interface/src/ui/CachesSizeDialog.cpp b/interface/src/ui/CachesSizeDialog.cpp index a29793349f..dee9452c73 100644 --- a/interface/src/ui/CachesSizeDialog.cpp +++ b/interface/src/ui/CachesSizeDialog.cpp @@ -56,7 +56,7 @@ CachesSizeDialog::CachesSizeDialog(QWidget* parent) : void CachesSizeDialog::confirmClicked(bool checked) { DependencyManager::get()->setUnusedResourceCacheSize(_animations->value() * BYTES_PER_MEGABYTES); - DependencyManager::get()->setUnusedResourceCacheSize(_geometries->value() * BYTES_PER_MEGABYTES); + DependencyManager::get()->setUnusedResourceCacheSize(_geometries->value() * BYTES_PER_MEGABYTES); DependencyManager::get()->setUnusedResourceCacheSize(_sounds->value() * BYTES_PER_MEGABYTES); DependencyManager::get()->setUnusedResourceCacheSize(_textures->value() * BYTES_PER_MEGABYTES); @@ -65,7 +65,7 @@ void CachesSizeDialog::confirmClicked(bool checked) { void CachesSizeDialog::resetClicked(bool checked) { _animations->setValue(DependencyManager::get()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES); - _geometries->setValue(DependencyManager::get()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES); + _geometries->setValue(DependencyManager::get()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES); _sounds->setValue(DependencyManager::get()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES); _textures->setValue(DependencyManager::get()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES); } diff --git a/libraries/entities-renderer/CMakeLists.txt b/libraries/entities-renderer/CMakeLists.txt index 3787beb32b..695ca1feb5 100644 --- a/libraries/entities-renderer/CMakeLists.txt +++ b/libraries/entities-renderer/CMakeLists.txt @@ -26,4 +26,4 @@ find_package(PolyVox REQUIRED) target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${POLYVOX_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${POLYVOX_LIBRARIES}) -link_hifi_libraries(shared gpu gpu-networking procedural script-engine render render-utils) +link_hifi_libraries(shared gpu procedural model model-networking script-engine render render-utils) diff --git a/libraries/gpu/src/gpu/Shader.cpp b/libraries/gpu/src/gpu/Shader.cpp index 59838fae9c..223a78ed93 100755 --- a/libraries/gpu/src/gpu/Shader.cpp +++ b/libraries/gpu/src/gpu/Shader.cpp @@ -71,3 +71,28 @@ bool Shader::makeProgram(Shader& shader, const Shader::BindingSet& bindings) { } return false; } + + +// ShaderSource +ShaderSource::ShaderSource() { +} + +ShaderSource::~ShaderSource() { +} + +void ShaderSource::reset(const QUrl& url) { + _shaderUrl = url; + _gpuShader.reset(); +} + +void ShaderSource::resetShader(gpu::Shader* shader) { + _gpuShader.reset(shader); +} + +bool ShaderSource::isDefined() const { + if (_gpuShader) { + return true; + } else { + return false; + } +} diff --git a/libraries/gpu/src/gpu/Shader.h b/libraries/gpu/src/gpu/Shader.h index 9c3953bff5..d3c3992e6d 100755 --- a/libraries/gpu/src/gpu/Shader.h +++ b/libraries/gpu/src/gpu/Shader.h @@ -15,6 +15,8 @@ #include #include #include + +#include namespace gpu { @@ -187,6 +189,27 @@ protected: typedef Shader::Pointer ShaderPointer; typedef std::vector< ShaderPointer > Shaders; +// ShaderSource is the bridge between a URL or a a way to produce the final gpu::Shader that will be used to render it. +class ShaderSource { +public: + ShaderSource(); + ~ShaderSource(); + + const QUrl& getUrl() const { return _shaderUrl; } + const gpu::ShaderPointer getGPUShader() const { return _gpuShader; } + + void reset(const QUrl& url); + + void resetShader(gpu::Shader* texture); + + bool isDefined() const; + +protected: + gpu::ShaderPointer _gpuShader; + QUrl _shaderUrl; +}; +typedef std::shared_ptr< ShaderSource > ShaderSourcePointer; + }; diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 100ad28053..57df7b8ec0 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -768,3 +768,28 @@ void SphericalHarmonics::evalFromTexture(const Texture& texture) { L22 = coefs[8]; } } + + +// TextureSource +TextureSource::TextureSource() { +} + +TextureSource::~TextureSource() { +} + +void TextureSource::reset(const QUrl& url) { + _imageUrl = url; +} + +void TextureSource::resetTexture(gpu::Texture* texture) { + _gpuTexture.reset(texture); +} + +bool TextureSource::isDefined() const { + if (_gpuTexture) { + return _gpuTexture->isDefined(); + } else { + return false; + } +} + diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 65a0439864..e1855e0848 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -15,6 +15,8 @@ #include //min max and more +#include + namespace gpu { // THe spherical harmonics is a nice tool for cubemap, so if required, the irradiance SH can be automatically generated @@ -441,6 +443,28 @@ public: }; typedef std::vector TextureViews; +// TextureSource is the bridge between a URL or a a way to produce an image and the final gpu::Texture that will be used to render it. +// It provides the mechanism to create a texture using a customizable TextureLoader +class TextureSource { +public: + TextureSource(); + ~TextureSource(); + + const QUrl& getUrl() const { return _imageUrl; } + const gpu::TexturePointer getGPUTexture() const { return _gpuTexture; } + + void reset(const QUrl& url); + + void resetTexture(gpu::Texture* texture); + + bool isDefined() const; + +protected: + gpu::TexturePointer _gpuTexture; + QUrl _imageUrl; +}; +typedef std::shared_ptr< TextureSource > TextureSourcePointer; + }; diff --git a/libraries/gpu-networking/CMakeLists.txt b/libraries/model-networking/CMakeLists.txt similarity index 89% rename from libraries/gpu-networking/CMakeLists.txt rename to libraries/model-networking/CMakeLists.txt index 75e0f61b3e..3613f76cff 100644 --- a/libraries/gpu-networking/CMakeLists.txt +++ b/libraries/model-networking/CMakeLists.txt @@ -1,4 +1,4 @@ -set(TARGET_NAME gpu-networking) +set(TARGET_NAME model-networking) # use setup_hifi_library macro to setup our project and link appropriate Qt modules setup_hifi_library() diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp new file mode 100644 index 0000000000..8fa3a9a234 --- /dev/null +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -0,0 +1,506 @@ +// +// ModelCache.cpp +// interface/src/renderer +// +// Created by Andrzej Kapolka on 6/21/13. +// Copyright 2013 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 "ModelCache.h" + +#include + +#include +#include + +#include +#include + +#include "TextureCache.h" +#include "ModelNetworkingLogging.h" + +#include "gpu/StandardShaderLib.h" + +#include "model/TextureMap.h" + +//#define WANT_DEBUG + +ModelCache::ModelCache() +{ + const qint64 GEOMETRY_DEFAULT_UNUSED_MAX_SIZE = DEFAULT_UNUSED_MAX_SIZE; + setUnusedResourceCacheSize(GEOMETRY_DEFAULT_UNUSED_MAX_SIZE); +} + +ModelCache::~ModelCache() { +} + +QSharedPointer ModelCache::createResource(const QUrl& url, const QSharedPointer& fallback, + bool delayLoad, const void* extra) { + // NetworkGeometry is no longer a subclass of Resource, but requires this method because, it is pure virtual. + assert(false); + return QSharedPointer(); +} + + +GeometryReader::GeometryReader(const QUrl& url, const QByteArray& data, const QVariantHash& mapping) : + _url(url), + _data(data), + _mapping(mapping) { +} + +void GeometryReader::run() { + try { + if (_data.isEmpty()) { + throw QString("Reply is NULL ?!"); + } + QString urlname = _url.path().toLower(); + bool urlValid = true; + urlValid &= !urlname.isEmpty(); + urlValid &= !_url.path().isEmpty(); + urlValid &= _url.path().toLower().endsWith(".fbx") || _url.path().toLower().endsWith(".obj"); + + if (urlValid) { + // Let's read the binaries from the network + FBXGeometry* fbxgeo = nullptr; + if (_url.path().toLower().endsWith(".fbx")) { + const bool grabLightmaps = true; + const float lightmapLevel = 1.0f; + fbxgeo = readFBX(_data, _mapping, _url.path(), grabLightmaps, lightmapLevel); + } else if (_url.path().toLower().endsWith(".obj")) { + fbxgeo = OBJReader().readOBJ(_data, _mapping, _url); + } else { + QString errorStr("usupported format"); + emit onError(NetworkGeometry::ModelParseError, errorStr); + } + emit onSuccess(fbxgeo); + } else { + throw QString("url is invalid"); + } + + } catch (const QString& error) { + qCDebug(modelnetworking) << "Error reading " << _url << ": " << error; + emit onError(NetworkGeometry::ModelParseError, error); + } +} + +NetworkGeometry::NetworkGeometry(const QUrl& url, bool delayLoad, const QVariantHash& mapping, const QUrl& textureBaseUrl) : + _url(url), + _mapping(mapping), + _textureBaseUrl(textureBaseUrl.isValid() ? textureBaseUrl : url) { + + if (delayLoad) { + _state = DelayState; + } else { + attemptRequestInternal(); + } +} + +NetworkGeometry::~NetworkGeometry() { + if (_resource) { + _resource->deleteLater(); + } +} + +void NetworkGeometry::attemptRequest() { + if (_state == DelayState) { + attemptRequestInternal(); + } +} + +void NetworkGeometry::attemptRequestInternal() { + if (_url.path().toLower().endsWith(".fst")) { + _mappingUrl = _url; + requestMapping(_url); + } else { + _modelUrl = _url; + requestModel(_url); + } +} + +bool NetworkGeometry::isLoaded() const { + return _state == SuccessState; +} + +bool NetworkGeometry::isLoadedWithTextures() const { + if (!isLoaded()) { + return false; + } + + if (!_isLoadedWithTextures) { + for (auto&& material : _materials) { + if ((material->diffuseTexture && !material->diffuseTexture->isLoaded()) || + (material->normalTexture && !material->normalTexture->isLoaded()) || + (material->specularTexture && !material->specularTexture->isLoaded()) || + (material->emissiveTexture && !material->emissiveTexture->isLoaded())) { + return false; + } + } + _isLoadedWithTextures = true; + } + return true; +} + +void NetworkGeometry::setTextureWithNameToURL(const QString& name, const QUrl& url) { + + + if (_meshes.size() > 0) { + auto textureCache = DependencyManager::get(); + for (auto&& material : _materials) { + QSharedPointer matchingTexture = QSharedPointer(); + if (material->diffuseTextureName == name) { + material->diffuseTexture = textureCache->getTexture(url, DEFAULT_TEXTURE); + } else if (material->normalTextureName == name) { + material->normalTexture = textureCache->getTexture(url); + } else if (material->specularTextureName == name) { + material->specularTexture = textureCache->getTexture(url); + } else if (material->emissiveTextureName == name) { + material->emissiveTexture = textureCache->getTexture(url); + } + } + } else { + qCWarning(modelnetworking) << "Ignoring setTextureWirthNameToURL() geometry not ready." << name << url; + } + _isLoadedWithTextures = false; +} + +QStringList NetworkGeometry::getTextureNames() const { + QStringList result; + for (auto&& material : _materials) { + if (!material->diffuseTextureName.isEmpty() && material->diffuseTexture) { + QString textureURL = material->diffuseTexture->getURL().toString(); + result << material->diffuseTextureName + ":" + textureURL; + } + + if (!material->normalTextureName.isEmpty() && material->normalTexture) { + QString textureURL = material->normalTexture->getURL().toString(); + result << material->normalTextureName + ":" + textureURL; + } + + if (!material->specularTextureName.isEmpty() && material->specularTexture) { + QString textureURL = material->specularTexture->getURL().toString(); + result << material->specularTextureName + ":" + textureURL; + } + + if (!material->emissiveTextureName.isEmpty() && material->emissiveTexture) { + QString textureURL = material->emissiveTexture->getURL().toString(); + result << material->emissiveTextureName + ":" + textureURL; + } + } + + return result; +} + +void NetworkGeometry::requestMapping(const QUrl& url) { + _state = RequestMappingState; + if (_resource) { + _resource->deleteLater(); + } + _resource = new Resource(url, false); + connect(_resource, &Resource::loaded, this, &NetworkGeometry::mappingRequestDone); + connect(_resource, &Resource::failed, this, &NetworkGeometry::mappingRequestError); +} + +void NetworkGeometry::requestModel(const QUrl& url) { + _state = RequestModelState; + if (_resource) { + _resource->deleteLater(); + } + _modelUrl = url; + _resource = new Resource(url, false); + connect(_resource, &Resource::loaded, this, &NetworkGeometry::modelRequestDone); + connect(_resource, &Resource::failed, this, &NetworkGeometry::modelRequestError); +} + +void NetworkGeometry::mappingRequestDone(const QByteArray& data) { + assert(_state == RequestMappingState); + + // parse the mapping file + _mapping = FSTReader::readMapping(data); + + QUrl replyUrl = _mappingUrl; + QString modelUrlStr = _mapping.value("filename").toString(); + if (modelUrlStr.isNull()) { + qCDebug(modelnetworking) << "Mapping file " << _url << "has no \"filename\" entry"; + emit onFailure(*this, MissingFilenameInMapping); + } else { + // read _textureBase from mapping file, if present + QString texdir = _mapping.value("texdir").toString(); + if (!texdir.isNull()) { + if (!texdir.endsWith('/')) { + texdir += '/'; + } + _textureBaseUrl = replyUrl.resolved(texdir); + } + + _modelUrl = replyUrl.resolved(modelUrlStr); + requestModel(_modelUrl); + } +} + +void NetworkGeometry::mappingRequestError(QNetworkReply::NetworkError error) { + assert(_state == RequestMappingState); + _state = ErrorState; + emit onFailure(*this, MappingRequestError); +} + +void NetworkGeometry::modelRequestDone(const QByteArray& data) { + assert(_state == RequestModelState); + + _state = ParsingModelState; + + // asynchronously parse the model file. + GeometryReader* geometryReader = new GeometryReader(_modelUrl, data, _mapping); + connect(geometryReader, SIGNAL(onSuccess(FBXGeometry*)), SLOT(modelParseSuccess(FBXGeometry*))); + connect(geometryReader, SIGNAL(onError(int, QString)), SLOT(modelParseError(int, QString))); + + QThreadPool::globalInstance()->start(geometryReader); +} + +void NetworkGeometry::modelRequestError(QNetworkReply::NetworkError error) { + assert(_state == RequestModelState); + _state = ErrorState; + emit onFailure(*this, ModelRequestError); +} + +static NetworkMesh* buildNetworkMesh(const FBXMesh& mesh, const QUrl& textureBaseUrl) { + auto textureCache = DependencyManager::get(); + NetworkMesh* networkMesh = new NetworkMesh(); + + int totalIndices = 0; + bool checkForTexcoordLightmap = false; + + + + // process network parts + foreach (const FBXMeshPart& part, mesh.parts) { + totalIndices += (part.quadIndices.size() + part.triangleIndices.size()); + } + + // initialize index buffer + { + networkMesh->_indexBuffer = std::make_shared(); + networkMesh->_indexBuffer->resize(totalIndices * sizeof(int)); + int offset = 0; + foreach(const FBXMeshPart& part, mesh.parts) { + networkMesh->_indexBuffer->setSubData(offset, part.quadIndices.size() * sizeof(int), + (gpu::Byte*) part.quadIndices.constData()); + offset += part.quadIndices.size() * sizeof(int); + networkMesh->_indexBuffer->setSubData(offset, part.triangleIndices.size() * sizeof(int), + (gpu::Byte*) part.triangleIndices.constData()); + offset += part.triangleIndices.size() * sizeof(int); + } + } + + // initialize vertex buffer + { + networkMesh->_vertexBuffer = std::make_shared(); + // if we don't need to do any blending, the positions/normals can be static + if (mesh.blendshapes.isEmpty()) { + int normalsOffset = mesh.vertices.size() * sizeof(glm::vec3); + int tangentsOffset = normalsOffset + mesh.normals.size() * sizeof(glm::vec3); + int colorsOffset = tangentsOffset + mesh.tangents.size() * sizeof(glm::vec3); + int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3); + int texCoords1Offset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2); + int clusterIndicesOffset = texCoords1Offset + mesh.texCoords1.size() * sizeof(glm::vec2); + int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4); + + networkMesh->_vertexBuffer->resize(clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4)); + + networkMesh->_vertexBuffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.vertices.constData()); + networkMesh->_vertexBuffer->setSubData(normalsOffset, mesh.normals.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.normals.constData()); + networkMesh->_vertexBuffer->setSubData(tangentsOffset, + mesh.tangents.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.tangents.constData()); + networkMesh->_vertexBuffer->setSubData(colorsOffset, mesh.colors.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.colors.constData()); + networkMesh->_vertexBuffer->setSubData(texCoordsOffset, + mesh.texCoords.size() * sizeof(glm::vec2), (gpu::Byte*) mesh.texCoords.constData()); + networkMesh->_vertexBuffer->setSubData(texCoords1Offset, + mesh.texCoords1.size() * sizeof(glm::vec2), (gpu::Byte*) mesh.texCoords1.constData()); + networkMesh->_vertexBuffer->setSubData(clusterIndicesOffset, + mesh.clusterIndices.size() * sizeof(glm::vec4), (gpu::Byte*) mesh.clusterIndices.constData()); + networkMesh->_vertexBuffer->setSubData(clusterWeightsOffset, + mesh.clusterWeights.size() * sizeof(glm::vec4), (gpu::Byte*) mesh.clusterWeights.constData()); + + // otherwise, at least the cluster indices/weights can be static + networkMesh->_vertexStream = std::make_shared(); + networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, 0, sizeof(glm::vec3)); + if (mesh.normals.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, normalsOffset, sizeof(glm::vec3)); + if (mesh.tangents.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, tangentsOffset, sizeof(glm::vec3)); + if (mesh.colors.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, colorsOffset, sizeof(glm::vec3)); + if (mesh.texCoords.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, texCoordsOffset, sizeof(glm::vec2)); + if (mesh.texCoords1.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, texCoords1Offset, sizeof(glm::vec2)); + if (mesh.clusterIndices.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, clusterIndicesOffset, sizeof(glm::vec4)); + if (mesh.clusterWeights.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, clusterWeightsOffset, sizeof(glm::vec4)); + + int channelNum = 0; + networkMesh->_vertexFormat = std::make_shared(); + networkMesh->_vertexFormat->setAttribute(gpu::Stream::POSITION, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); + if (mesh.normals.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::NORMAL, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); + if (mesh.tangents.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::TANGENT, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); + if (mesh.colors.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::COLOR, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RGB)); + if (mesh.texCoords.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::TEXCOORD, channelNum++, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); + if (mesh.texCoords1.size()) { + networkMesh->_vertexFormat->setAttribute(gpu::Stream::TEXCOORD1, channelNum++, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); + // } else if (checkForTexcoordLightmap && mesh.texCoords.size()) { + } else if (mesh.texCoords.size()) { + // need lightmap texcoord UV but doesn't have uv#1 so just reuse the same channel + networkMesh->_vertexFormat->setAttribute(gpu::Stream::TEXCOORD1, channelNum - 1, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); + } + if (mesh.clusterIndices.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_INDEX, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW)); + if (mesh.clusterWeights.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW)); + } + else { + int colorsOffset = mesh.tangents.size() * sizeof(glm::vec3); + int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3); + int clusterIndicesOffset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2); + int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4); + + networkMesh->_vertexBuffer->resize(clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4)); + networkMesh->_vertexBuffer->setSubData(0, mesh.tangents.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.tangents.constData()); + networkMesh->_vertexBuffer->setSubData(colorsOffset, mesh.colors.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.colors.constData()); + networkMesh->_vertexBuffer->setSubData(texCoordsOffset, + mesh.texCoords.size() * sizeof(glm::vec2), (gpu::Byte*) mesh.texCoords.constData()); + networkMesh->_vertexBuffer->setSubData(clusterIndicesOffset, + mesh.clusterIndices.size() * sizeof(glm::vec4), (gpu::Byte*) mesh.clusterIndices.constData()); + networkMesh->_vertexBuffer->setSubData(clusterWeightsOffset, + mesh.clusterWeights.size() * sizeof(glm::vec4), (gpu::Byte*) mesh.clusterWeights.constData()); + + networkMesh->_vertexStream = std::make_shared(); + if (mesh.tangents.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, 0, sizeof(glm::vec3)); + if (mesh.colors.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, colorsOffset, sizeof(glm::vec3)); + if (mesh.texCoords.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, texCoordsOffset, sizeof(glm::vec2)); + if (mesh.clusterIndices.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, clusterIndicesOffset, sizeof(glm::vec4)); + if (mesh.clusterWeights.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, clusterWeightsOffset, sizeof(glm::vec4)); + + int channelNum = 0; + networkMesh->_vertexFormat = std::make_shared(); + networkMesh->_vertexFormat->setAttribute(gpu::Stream::POSITION, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); + if (mesh.normals.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::NORMAL, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); + if (mesh.tangents.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::TANGENT, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); + if (mesh.colors.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::COLOR, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RGB)); + if (mesh.texCoords.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::TEXCOORD, channelNum++, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); + if (mesh.clusterIndices.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_INDEX, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW)); + if (mesh.clusterWeights.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW)); + } + } + + return networkMesh; +} + +static NetworkMaterial* buildNetworkMaterial(const FBXMaterial& material, const QUrl& textureBaseUrl) { + auto textureCache = DependencyManager::get(); + NetworkMaterial* networkMaterial = new NetworkMaterial(); + + int totalIndices = 0; + bool checkForTexcoordLightmap = false; + + networkMaterial->_material = material._material; + + if (!material.diffuseTexture.filename.isEmpty()) { + networkMaterial->diffuseTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(material.diffuseTexture.filename)), DEFAULT_TEXTURE, material.diffuseTexture.content); + networkMaterial->diffuseTextureName = material.diffuseTexture.name; + + auto diffuseMap = model::TextureMapPointer(new model::TextureMap()); + diffuseMap->setTextureSource(networkMaterial->diffuseTexture->_textureSource); + diffuseMap->setTextureTransform(material.diffuseTexture.transform); + + material._material->setTextureMap(model::MaterialKey::DIFFUSE_MAP, diffuseMap); + } + if (!material.normalTexture.filename.isEmpty()) { + networkMaterial->normalTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(material.normalTexture.filename)), (material.normalTexture.isBumpmap ? BUMP_TEXTURE : NORMAL_TEXTURE), material.normalTexture.content); + networkMaterial->normalTextureName = material.normalTexture.name; + + auto normalMap = model::TextureMapPointer(new model::TextureMap()); + normalMap->setTextureSource(networkMaterial->normalTexture->_textureSource); + + material._material->setTextureMap(model::MaterialKey::NORMAL_MAP, normalMap); + } + if (!material.specularTexture.filename.isEmpty()) { + networkMaterial->specularTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(material.specularTexture.filename)), SPECULAR_TEXTURE, material.specularTexture.content); + networkMaterial->specularTextureName = material.specularTexture.name; + + auto glossMap = model::TextureMapPointer(new model::TextureMap()); + glossMap->setTextureSource(networkMaterial->specularTexture->_textureSource); + + material._material->setTextureMap(model::MaterialKey::GLOSS_MAP, glossMap); + } + if (!material.emissiveTexture.filename.isEmpty()) { + networkMaterial->emissiveTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(material.emissiveTexture.filename)), EMISSIVE_TEXTURE, material.emissiveTexture.content); + networkMaterial->emissiveTextureName = material.emissiveTexture.name; + + checkForTexcoordLightmap = true; + + auto lightmapMap = model::TextureMapPointer(new model::TextureMap()); + lightmapMap->setTextureSource(networkMaterial->emissiveTexture->_textureSource); + lightmapMap->setTextureTransform(material.emissiveTexture.transform); + lightmapMap->setLightmapOffsetScale(material.emissiveParams.x, material.emissiveParams.y); + + material._material->setTextureMap(model::MaterialKey::LIGHTMAP_MAP, lightmapMap); + } + + return networkMaterial; +} + + +void NetworkGeometry::modelParseSuccess(FBXGeometry* geometry) { + // assume owner ship of geometry pointer + _geometry.reset(geometry); + + + + foreach(const FBXMesh& mesh, _geometry->meshes) { + _meshes.emplace_back(buildNetworkMesh(mesh, _textureBaseUrl)); + } + + QHash fbxMatIDToMatID; + foreach(const FBXMaterial& material, _geometry->materials) { + fbxMatIDToMatID[material.materialID] = _materials.size(); + _materials.emplace_back(buildNetworkMaterial(material, _textureBaseUrl)); + } + + + int meshID = 0; + foreach(const FBXMesh& mesh, _geometry->meshes) { + int partID = 0; + foreach (const FBXMeshPart& part, mesh.parts) { + NetworkShape* networkShape = new NetworkShape(); + networkShape->_meshID = meshID; + networkShape->_partID = partID; + networkShape->_materialID = fbxMatIDToMatID[part.materialID]; + _shapes.emplace_back(networkShape); + partID++; + } + meshID++; + } + + _state = SuccessState; + emit onSuccess(*this, *_geometry.get()); + + delete _resource; + _resource = nullptr; +} + +void NetworkGeometry::modelParseError(int error, QString str) { + _state = ErrorState; + emit onFailure(*this, (NetworkGeometry::Error)error); + + delete _resource; + _resource = nullptr; +} + + +const NetworkMaterial* NetworkGeometry::getShapeMaterial(int shapeID) { + if ((shapeID >= 0) && (shapeID < _shapes.size())) { + int materialID = _shapes[shapeID]->_materialID; + if ((materialID >= 0) && (materialID < _materials.size())) { + return _materials[materialID].get(); + } else { + return 0; + } + } else { + return 0; + } +} + diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h new file mode 100644 index 0000000000..1110d36e3e --- /dev/null +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -0,0 +1,205 @@ +// +// ModelCache.h +// libraries/model-networking/src/model-networking +// +// Created by Sam Gateau on 9/21/15. +// Copyright 2013 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_ModelCache_h +#define hifi_ModelCache_h + +#include +#include + +#include +#include + +#include "FBXReader.h" +#include "OBJReader.h" + +#include +#include + + +#include +#include + +class NetworkGeometry; +class NetworkMesh; +class NetworkTexture; +class NetworkMaterial; +class NetworkShape; + +/// Stores cached geometry. +class ModelCache : public ResourceCache, public Dependency { + Q_OBJECT + SINGLETON_DEPENDENCY + +public: + virtual QSharedPointer createResource(const QUrl& url, const QSharedPointer& fallback, + bool delayLoad, const void* extra); + + /// Loads geometry from the specified URL. + /// \param fallback a fallback URL to load if the desired one is unavailable + /// \param delayLoad if true, don't load the geometry immediately; wait until load is first requested + QSharedPointer getGeometry(const QUrl& url, const QUrl& fallback = QUrl(), bool delayLoad = false); + + /// Set a batch to the simple pipeline, returning the previous pipeline + void useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend = false); + +private: + ModelCache(); + virtual ~ModelCache(); + + QHash > _networkGeometry; +}; + +class NetworkGeometry : public QObject { + Q_OBJECT + +public: + // mapping is only used if url is a .fbx or .obj file, it is essentially the content of an fst file. + // if delayLoad is true, the url will not be immediately downloaded. + // use the attemptRequest method to initiate the download. + NetworkGeometry(const QUrl& url, bool delayLoad, const QVariantHash& mapping, const QUrl& textureBaseUrl = QUrl()); + ~NetworkGeometry(); + + const QUrl& getURL() const { return _url; } + + void attemptRequest(); + + // true when the geometry is loaded (but maybe not it's associated textures) + bool isLoaded() const; + + // true when the requested geometry and its textures are loaded. + bool isLoadedWithTextures() const; + + // WARNING: only valid when isLoaded returns true. + const FBXGeometry& getFBXGeometry() const { return *_geometry; } + const std::vector>& getMeshes() const { return _meshes; } + // const model::AssetPointer getAsset() const { return _asset; } + + // model::MeshPointer getShapeMesh(int shapeID); + // int getShapePart(int shapeID); + + // This would be the final verison + // model::MaterialPointer getShapeMaterial(int shapeID); + const NetworkMaterial* getShapeMaterial(int shapeID); + + + void setTextureWithNameToURL(const QString& name, const QUrl& url); + QStringList getTextureNames() const; + + enum Error { + MissingFilenameInMapping = 0, + MappingRequestError, + ModelRequestError, + ModelParseError + }; + +signals: + // Fired when everything has downloaded and parsed successfully. + void onSuccess(NetworkGeometry& networkGeometry, FBXGeometry& fbxGeometry); + + // Fired when something went wrong. + void onFailure(NetworkGeometry& networkGeometry, Error error); + +protected slots: + void mappingRequestDone(const QByteArray& data); + void mappingRequestError(QNetworkReply::NetworkError error); + + void modelRequestDone(const QByteArray& data); + void modelRequestError(QNetworkReply::NetworkError error); + + void modelParseSuccess(FBXGeometry* geometry); + void modelParseError(int error, QString str); + +protected: + void attemptRequestInternal(); + void requestMapping(const QUrl& url); + void requestModel(const QUrl& url); + + enum State { DelayState, + RequestMappingState, + RequestModelState, + ParsingModelState, + SuccessState, + ErrorState }; + State _state; + + QUrl _url; + QUrl _mappingUrl; + QUrl _modelUrl; + QVariantHash _mapping; + QUrl _textureBaseUrl; + + Resource* _resource = nullptr; + std::unique_ptr _geometry; // This should go away evenutally once we can put everything we need in the model::AssetPointer + std::vector> _meshes; + std::vector> _materials; + std::vector> _shapes; + + + // The model asset created from this NetworkGeometry + // model::AssetPointer _asset; + + // cache for isLoadedWithTextures() + mutable bool _isLoadedWithTextures = false; +}; + +/// Reads geometry in a worker thread. +class GeometryReader : public QObject, public QRunnable { + Q_OBJECT +public: + GeometryReader(const QUrl& url, const QByteArray& data, const QVariantHash& mapping); + virtual void run(); +signals: + void onSuccess(FBXGeometry* geometry); + void onError(int error, QString str); +private: + QUrl _url; + QByteArray _data; + QVariantHash _mapping; +}; + + +class NetworkShape { +public: + int _meshID{ -1 }; + int _partID{ -1 }; + int _materialID{ -1 }; +}; + +class NetworkMaterial { +public: + model::MaterialPointer _material; + QString diffuseTextureName; + QSharedPointer diffuseTexture; + QString normalTextureName; + QSharedPointer normalTexture; + QString specularTextureName; + QSharedPointer specularTexture; + QString emissiveTextureName; + QSharedPointer emissiveTexture; +}; + + +/// The state associated with a single mesh. +class NetworkMesh { +public: + gpu::BufferPointer _indexBuffer; + gpu::BufferPointer _vertexBuffer; + + gpu::BufferStreamPointer _vertexStream; + + gpu::Stream::FormatPointer _vertexFormat; + + int getTranslucentPartCount(const FBXMesh& fbxMesh) const; + bool isPartTranslucent(const FBXMesh& fbxMesh, int partIndex) const; +}; + +#endif // hifi_GeometryCache_h diff --git a/libraries/gpu-networking/src/gpu-networking/GpuNetworkingLogging.cpp b/libraries/model-networking/src/model-networking/ModelNetworkingLogging.cpp similarity index 73% rename from libraries/gpu-networking/src/gpu-networking/GpuNetworkingLogging.cpp rename to libraries/model-networking/src/model-networking/ModelNetworkingLogging.cpp index 38da22969b..0c44fa33eb 100644 --- a/libraries/gpu-networking/src/gpu-networking/GpuNetworkingLogging.cpp +++ b/libraries/model-networking/src/model-networking/ModelNetworkingLogging.cpp @@ -6,6 +6,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "GpuNetworkingLogging.h" +#include "ModelNetworkingLogging.h" -Q_LOGGING_CATEGORY(gpunetwork, "hifi.gpu-network") +Q_LOGGING_CATEGORY(modelnetworking, "hifi.gpu-network") diff --git a/libraries/gpu-networking/src/gpu-networking/GpuNetworkingLogging.h b/libraries/model-networking/src/model-networking/ModelNetworkingLogging.h similarity index 86% rename from libraries/gpu-networking/src/gpu-networking/GpuNetworkingLogging.h rename to libraries/model-networking/src/model-networking/ModelNetworkingLogging.h index 7499340a9b..caf997bad0 100644 --- a/libraries/gpu-networking/src/gpu-networking/GpuNetworkingLogging.h +++ b/libraries/model-networking/src/model-networking/ModelNetworkingLogging.h @@ -8,4 +8,4 @@ #include -Q_DECLARE_LOGGING_CATEGORY(gpunetwork) +Q_DECLARE_LOGGING_CATEGORY(modelnetworking) diff --git a/libraries/gpu-networking/src/gpu-networking/ShaderCache.cpp b/libraries/model-networking/src/model-networking/ShaderCache.cpp similarity index 100% rename from libraries/gpu-networking/src/gpu-networking/ShaderCache.cpp rename to libraries/model-networking/src/model-networking/ShaderCache.cpp diff --git a/libraries/gpu-networking/src/gpu-networking/ShaderCache.h b/libraries/model-networking/src/model-networking/ShaderCache.h similarity index 100% rename from libraries/gpu-networking/src/gpu-networking/ShaderCache.h rename to libraries/model-networking/src/model-networking/ShaderCache.h diff --git a/libraries/gpu-networking/src/gpu-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp similarity index 94% rename from libraries/gpu-networking/src/gpu-networking/TextureCache.cpp rename to libraries/model-networking/src/model-networking/TextureCache.cpp index fd69a2ab04..0bdc0749f6 100644 --- a/libraries/gpu-networking/src/gpu-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -1,6 +1,6 @@ // // TextureCache.cpp -// libraries/gpu-networking/src +// libraries/model-networking/src // // Created by Andrzej Kapolka on 8/6/13. // Copyright 2013 High Fidelity, Inc. @@ -25,7 +25,7 @@ #include -#include "GpuNetworkingLogging.h" +#include "ModelNetworkingLogging.h" TextureCache::TextureCache() { const qint64 TEXTURE_DEFAULT_UNUSED_MAX_SIZE = DEFAULT_UNUSED_MAX_SIZE; @@ -185,7 +185,7 @@ NetworkTexture::NetworkTexture(const QUrl& url, TextureType type, const QByteArr _width(0), _height(0) { - _textureSource.reset(new model::TextureSource()); + _textureSource.reset(new gpu::TextureSource()); if (!url.isValid()) { _loaded = true; @@ -206,7 +206,7 @@ NetworkTexture::NetworkTexture(const QUrl& url, const TextureLoaderFunc& texture _width(0), _height(0) { - _textureSource.reset(new model::TextureSource()); + _textureSource.reset(new gpu::TextureSource()); if (!url.isValid()) { _loaded = true; @@ -223,11 +223,11 @@ NetworkTexture::NetworkTexture(const QUrl& url, const TextureLoaderFunc& texture NetworkTexture::TextureLoaderFunc NetworkTexture::getTextureLoader() const { switch (_type) { case CUBE_TEXTURE: { - return TextureLoaderFunc(model::TextureSource::createCubeTextureFromImage); + return TextureLoaderFunc(model::TextureUsage::createCubeTextureFromImage); break; } case BUMP_TEXTURE: { - return TextureLoaderFunc(model::TextureSource::createNormalTextureFromBumpImage); + return TextureLoaderFunc(model::TextureUsage::createNormalTextureFromBumpImage); break; } case CUSTOM_TEXTURE: { @@ -239,7 +239,7 @@ NetworkTexture::TextureLoaderFunc NetworkTexture::getTextureLoader() const { case SPECULAR_TEXTURE: case EMISSIVE_TEXTURE: default: { - return TextureLoaderFunc(model::TextureSource::create2DTextureFromImage); + return TextureLoaderFunc(model::TextureUsage::create2DTextureFromImage); break; } } @@ -288,7 +288,7 @@ void listSupportedImageFormats() { foreach(const QByteArray& f, supportedFormats) { formats += QString(f) + ","; } - qCDebug(gpunetwork) << "List of supported Image formats:" << formats; + qCDebug(modelnetworking) << "List of supported Image formats:" << formats; }); } @@ -313,9 +313,9 @@ void ImageReader::run() { if (originalWidth == 0 || originalHeight == 0 || imageFormat == QImage::Format_Invalid) { if (filenameExtension.empty()) { - qCDebug(gpunetwork) << "QImage failed to create from content, no file extension:" << _url; + qCDebug(modelnetworking) << "QImage failed to create from content, no file extension:" << _url; } else { - qCDebug(gpunetwork) << "QImage failed to create from content" << _url; + qCDebug(modelnetworking) << "QImage failed to create from content" << _url; } return; } diff --git a/libraries/gpu-networking/src/gpu-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h similarity index 98% rename from libraries/gpu-networking/src/gpu-networking/TextureCache.h rename to libraries/model-networking/src/model-networking/TextureCache.h index 335ea2d89c..cc22509631 100644 --- a/libraries/gpu-networking/src/gpu-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -1,6 +1,6 @@ // // TextureCache.h -// libraries/gpu-networking/src +// libraries/model-networking/src // // Created by Andrzej Kapolka on 8/6/13. // Copyright 2013 High Fidelity, Inc. @@ -98,7 +98,7 @@ public: ~Texture(); const gpu::TexturePointer getGPUTexture() const { return _textureSource->getGPUTexture(); } - model::TextureSourcePointer _textureSource; + gpu::TextureSourcePointer _textureSource; protected: diff --git a/libraries/model/CMakeLists.txt b/libraries/model/CMakeLists.txt index 5e8ebb247c..c30ffb7238 100755 --- a/libraries/model/CMakeLists.txt +++ b/libraries/model/CMakeLists.txt @@ -9,4 +9,4 @@ add_dependency_external_projects(glm) find_package(GLM REQUIRED) target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS}) -link_hifi_libraries(shared networking gpu octree) +link_hifi_libraries(shared gpu) diff --git a/libraries/model/src/model/Skybox.cpp b/libraries/model/src/model/Skybox.cpp index 7e3af09a1f..c931b78128 100755 --- a/libraries/model/src/model/Skybox.cpp +++ b/libraries/model/src/model/Skybox.cpp @@ -13,7 +13,6 @@ #include #include -// #include #include #include "Skybox_vert.h" @@ -39,16 +38,7 @@ Skybox::Skybox() { void Skybox::setColor(const Color& color) { _color = color; } -/* -void Skybox::setProcedural(QSharedPointer procedural) { - _procedural = procedural; - if (_procedural) { - _procedural->_vertexSource = Skybox_vert; - _procedural->_fragmentSource = Skybox_frag; - // No pipeline state customization - } -} -*/ + void Skybox::setCubemap(const gpu::TexturePointer& cubemap) { _cubemap = cubemap; } @@ -58,7 +48,7 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky static gpu::BufferPointer theBuffer; static gpu::Stream::FormatPointer theFormat; - if (/*skybox._procedural || */skybox.getCubemap()) { + if (skybox._procedural || skybox.getCubemap()) { if (!theBuffer) { const float CLIP = 1.0f; const glm::vec2 vertices[4] = { { -CLIP, -CLIP }, { CLIP, -CLIP }, { -CLIP, CLIP }, { CLIP, CLIP } }; @@ -78,14 +68,7 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky batch.setInputBuffer(gpu::Stream::POSITION, theBuffer, 0, 8); batch.setInputFormat(theFormat); - /*if (skybox._procedural && skybox._procedural->_enabled && skybox._procedural->ready()) { - if (skybox.getCubemap() && skybox.getCubemap()->isDefined()) { - batch.setResourceTexture(0, skybox.getCubemap()); - } - - skybox._procedural->prepare(batch, glm::vec3(1)); - batch.draw(gpu::TRIANGLE_STRIP, 4); - } else*/ if (skybox.getCubemap() && skybox.getCubemap()->isDefined()) { + if (skybox.getCubemap() && skybox.getCubemap()->isDefined()) { static gpu::BufferPointer theConstants; static gpu::PipelinePointer thePipeline; static int SKYBOX_CONSTANTS_SLOT = 0; // need to be defined by the compilation of the shader diff --git a/libraries/model/src/model/Skybox.h b/libraries/model/src/model/Skybox.h index 887f1ff80b..68352e4309 100755 --- a/libraries/model/src/model/Skybox.h +++ b/libraries/model/src/model/Skybox.h @@ -11,13 +11,14 @@ #ifndef hifi_model_Skybox_h #define hifi_model_Skybox_h -//#include #include #include "Light.h" class ViewFrustum; struct Procedural; +typedef std::shared_ptr ProceduralPointer; + namespace gpu { class Batch; } namespace model { @@ -36,13 +37,13 @@ public: void setCubemap(const gpu::TexturePointer& cubemap); const gpu::TexturePointer& getCubemap() const { return _cubemap; } - // void setProcedural(QSharedPointer procedural); + void setProcedural(const ProceduralPointer& procedural); static void render(gpu::Batch& batch, const ViewFrustum& frustum, const Skybox& skybox); protected: gpu::TexturePointer _cubemap; - // QSharedPointer _procedural; + ProceduralPointer _procedural; Color _color{1.0f, 1.0f, 1.0f}; }; typedef std::shared_ptr< Skybox > SkyboxPointer; diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 0dd7bca754..c2551d276d 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -19,35 +19,9 @@ using namespace model; using namespace gpu; -// TextureSource -TextureSource::TextureSource() -{/* : Texture::Storage()//, - // _gpuTexture(Texture::createFromStorage(this))*/ -} -TextureSource::~TextureSource() { -} - -void TextureSource::reset(const QUrl& url, const TextureUsage& usage) { - _imageUrl = url; - _usage = usage; -} - -void TextureSource::resetTexture(gpu::Texture* texture) { - _gpuTexture.reset(texture); -} - -bool TextureSource::isDefined() const { - if (_gpuTexture) { - return _gpuTexture->isDefined(); - } else { - return false; - } -} - - -void TextureMap::setTextureSource(TextureSourcePointer& texStorage) { - _textureSource = texStorage; +void TextureMap::setTextureSource(TextureSourcePointer& textureSource) { + _textureSource = textureSource; } bool TextureMap::isDefined() const { @@ -78,7 +52,7 @@ void TextureMap::setLightmapOffsetScale(float offset, float scale) { -gpu::Texture* TextureSource::create2DTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { +gpu::Texture* TextureUsage::create2DTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { QImage image = srcImage; int imageArea = image.width() * image.height(); @@ -175,7 +149,7 @@ double mapComponent(double sobelValue) { return (sobelValue + 1.0) * factor; } -gpu::Texture* TextureSource::createNormalTextureFromBumpImage(const QImage& srcImage, const std::string& srcImageName) { +gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcImage, const std::string& srcImageName) { QImage image = srcImage; // PR 5540 by AlessandroSigna @@ -285,7 +259,7 @@ public: _faceZNeg(fZN) {} }; -gpu::Texture* TextureSource::createCubeTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { +gpu::Texture* TextureUsage::createCubeTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { QImage image = srcImage; int imageArea = image.width() * image.height(); diff --git a/libraries/model/src/model/TextureMap.h b/libraries/model/src/model/TextureMap.h index 197172358b..343ef1f481 100755 --- a/libraries/model/src/model/TextureMap.h +++ b/libraries/model/src/model/TextureMap.h @@ -30,36 +30,11 @@ public: Material::MapFlags _materialUsage{ MaterialKey::DIFFUSE_MAP }; int _environmentUsage = 0; -}; -// TextureSource is a specialized version of the gpu::Texture::Storage -// It provides the mechanism to create a texture from a Url and the intended usage -// that guides the internal format used -class TextureSource { -public: - TextureSource(); - ~TextureSource(); - - const QUrl& getUrl() const { return _imageUrl; } - gpu::Texture::Type getType() const { return _usage._type; } - const gpu::TexturePointer getGPUTexture() const { return _gpuTexture; } - - void reset(const QUrl& url, const TextureUsage& usage); - - void resetTexture(gpu::Texture* texture); - - bool isDefined() const; - static gpu::Texture* create2DTextureFromImage(const QImage& image, const std::string& srcImageName); static gpu::Texture* createNormalTextureFromBumpImage(const QImage& image, const std::string& srcImageName); static gpu::Texture* createCubeTextureFromImage(const QImage& image, const std::string& srcImageName); - -protected: - gpu::TexturePointer _gpuTexture; - TextureUsage _usage; - QUrl _imageUrl; }; -typedef std::shared_ptr< TextureSource > TextureSourcePointer; @@ -67,7 +42,7 @@ class TextureMap { public: TextureMap() {} - void setTextureSource(TextureSourcePointer& texStorage); + void setTextureSource(gpu::TextureSourcePointer& textureSource); bool isDefined() const; gpu::TextureView getTextureView() const; @@ -79,7 +54,7 @@ public: const glm::vec2& getLightmapOffsetScale() const { return _lightmapOffsetScale; } protected: - TextureSourcePointer _textureSource; + gpu::TextureSourcePointer _textureSource; Transform _texcoordTransform; glm::vec2 _lightmapOffsetScale{ 0.0f, 1.0f }; diff --git a/libraries/procedural/CMakeLists.txt b/libraries/procedural/CMakeLists.txt index bd53f0abb9..9595ef5f7a 100644 --- a/libraries/procedural/CMakeLists.txt +++ b/libraries/procedural/CMakeLists.txt @@ -1,5 +1,7 @@ set(TARGET_NAME procedural) +AUTOSCRIBE_SHADER_LIB(gpu model) + # use setup_hifi_library macro to setup our project and link appropriate Qt modules setup_hifi_library() @@ -7,4 +9,4 @@ add_dependency_external_projects(glm) find_package(GLM REQUIRED) target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS}) -link_hifi_libraries(shared gpu networking gpu-networking) +link_hifi_libraries(shared gpu model model-networking) diff --git a/libraries/procedural/src/procedural/Procedural.cpp b/libraries/procedural/src/procedural/Procedural.cpp index c46add8412..aa8946f62b 100644 --- a/libraries/procedural/src/procedural/Procedural.cpp +++ b/libraries/procedural/src/procedural/Procedural.cpp @@ -14,7 +14,6 @@ #include #include -#include #include #include #include diff --git a/libraries/procedural/src/procedural/Procedural.h b/libraries/procedural/src/procedural/Procedural.h index bb6a0ad44d..5b3f2b4742 100644 --- a/libraries/procedural/src/procedural/Procedural.h +++ b/libraries/procedural/src/procedural/Procedural.h @@ -18,8 +18,7 @@ #include #include #include -#include - +#include // FIXME better encapsulation // FIXME better mechanism for extending to things rendered using shaders other than simple.slv diff --git a/libraries/procedural/src/procedural/ProceduralSkybox.cpp b/libraries/procedural/src/procedural/ProceduralSkybox.cpp new file mode 100644 index 0000000000..e237bd3d7d --- /dev/null +++ b/libraries/procedural/src/procedural/ProceduralSkybox.cpp @@ -0,0 +1,71 @@ +// +// ProceduralSkybox.cpp +// libraries/procedural/src/procedural +// +// Created by Sam Gateau on 9/21/2015. +// Copyright 2015 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 "ProceduralSkybox.h" + + +#include +#include +#include + +#include "ProceduralSkybox_vert.h" +#include "ProceduralSkybox_frag.h" + +ProceduralSkybox::ProceduralSkybox() : model::Skybox() { +} + +void ProceduralSkybox::setProcedural(const ProceduralPointer& procedural) { + _procedural = procedural; + if (_procedural) { + _procedural->_vertexSource = ProceduralSkybox_vert; + _procedural->_fragmentSource = ProceduralSkybox_frag; + // No pipeline state customization + } +} + +void ProceduralSkybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const ProceduralSkybox& skybox) { + static gpu::BufferPointer theBuffer; + static gpu::Stream::FormatPointer theFormat; + + if (skybox._procedural || skybox.getCubemap()) { + if (!theBuffer) { + const float CLIP = 1.0f; + const glm::vec2 vertices[4] = { { -CLIP, -CLIP }, { CLIP, -CLIP }, { -CLIP, CLIP }, { CLIP, CLIP } }; + theBuffer = std::make_shared(sizeof(vertices), (const gpu::Byte*) vertices); + theFormat = std::make_shared(); + theFormat->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ)); + } + + glm::mat4 projMat; + viewFrustum.evalProjectionMatrix(projMat); + + Transform viewTransform; + viewFrustum.evalViewTransform(viewTransform); + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewTransform); + batch.setModelTransform(Transform()); // only for Mac + batch.setInputBuffer(gpu::Stream::POSITION, theBuffer, 0, 8); + batch.setInputFormat(theFormat); + + if (skybox._procedural && skybox._procedural->_enabled && skybox._procedural->ready()) { + if (skybox.getCubemap() && skybox.getCubemap()->isDefined()) { + batch.setResourceTexture(0, skybox.getCubemap()); + } + + skybox._procedural->prepare(batch, glm::vec3(1)); + batch.draw(gpu::TRIANGLE_STRIP, 4); + } + } else { + // skybox has no cubemap, just clear the color buffer + auto color = skybox.getColor(); + batch.clearFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(color, 0.0f), 0.0f, 0, true); + } +} + diff --git a/libraries/procedural/src/procedural/ProceduralSkybox.h b/libraries/procedural/src/procedural/ProceduralSkybox.h new file mode 100644 index 0000000000..32d8200079 --- /dev/null +++ b/libraries/procedural/src/procedural/ProceduralSkybox.h @@ -0,0 +1,35 @@ +// +// ProceduralSkybox.h +// libraries/procedural/src/procedural +// +// Created by Sam Gateau on 9/21/15. +// Copyright 2015 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 +// + +#pragma once +#ifndef hifi_ProceduralSkybox_h +#define hifi_ProceduralSkybox_h + +#include + +#include "Procedural.h" + +class ProceduralSkybox: public model::Skybox { +public: + ProceduralSkybox(); + ProceduralSkybox& operator= (const ProceduralSkybox& skybox); + virtual ~ProceduralSkybox() {}; + + void setProcedural(const ProceduralPointer& procedural); + + static void render(gpu::Batch& batch, const ViewFrustum& frustum, const ProceduralSkybox& skybox); + +protected: + ProceduralPointer _procedural; +}; +typedef std::shared_ptr< ProceduralSkybox > ProceduralSkyboxPointer; + +#endif diff --git a/libraries/procedural/src/procedural/ProceduralSkybox.slf b/libraries/procedural/src/procedural/ProceduralSkybox.slf new file mode 100644 index 0000000000..382801f52d --- /dev/null +++ b/libraries/procedural/src/procedural/ProceduralSkybox.slf @@ -0,0 +1,50 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// skybox.frag +// fragment shader +// +// Created by Sam Gateau on 5/5/2015. +// Copyright 2015 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 +// + +uniform samplerCube cubeMap; + +struct Skybox { + vec4 _color; +}; + +uniform skyboxBuffer { + Skybox _skybox; +}; + +in vec3 _normal; +out vec4 _fragColor; + +//PROCEDURAL_COMMON_BLOCK + +#line 1001 +//PROCEDURAL_BLOCK + +#line 2033 +void main(void) { + +#ifdef PROCEDURAL + + vec3 color = getSkyboxColor(); + _fragColor = vec4(color, 0.0); + +#else + + vec3 coord = normalize(_normal); + vec3 texel = texture(cubeMap, coord).rgb; + vec3 color = texel * _skybox._color.rgb; + vec3 pixel = pow(color, vec3(1.0/2.2)); // manual Gamma correction + _fragColor = vec4(pixel, 0.0); + +#endif + +} diff --git a/libraries/procedural/src/procedural/ProceduralSkybox.slv b/libraries/procedural/src/procedural/ProceduralSkybox.slv new file mode 100644 index 0000000000..d1b9a20a66 --- /dev/null +++ b/libraries/procedural/src/procedural/ProceduralSkybox.slv @@ -0,0 +1,34 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// skybox.vert +// vertex shader +// +// Created by Sam Gateau on 5/5/2015. +// Copyright 2015 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 gpu/Inputs.slh@> + +<@include gpu/Transform.slh@> + +<$declareStandardTransform()$> + +out vec3 _normal; + +void main(void) { + // standard transform + TransformCamera cam = getTransformCamera(); + vec3 clipDir = vec3(inPosition.xy, 0.0); + vec3 eyeDir; + + <$transformClipToEyeDir(cam, clipDir, eyeDir)$> + <$transformEyeToWorldDir(cam, eyeDir, _normal)$> + + // Position is supposed to come in clip space + gl_Position = vec4(inPosition.xy, 0.0, 1.0); +} \ No newline at end of file diff --git a/libraries/render-utils/CMakeLists.txt b/libraries/render-utils/CMakeLists.txt index 4d33c6f1c1..6a0f69dd8d 100644 --- a/libraries/render-utils/CMakeLists.txt +++ b/libraries/render-utils/CMakeLists.txt @@ -40,4 +40,4 @@ add_dependency_external_projects(oglplus) find_package(OGLPLUS REQUIRED) target_include_directories(${TARGET_NAME} PUBLIC ${OGLPLUS_INCLUDE_DIRS}) -link_hifi_libraries(shared gpu gpu-networking procedural model render environment animation fbx) +link_hifi_libraries(shared gpu procedural model model-networking render environment animation fbx) diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 77f9b2cece..89ac761a7a 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -38,8 +38,6 @@ const int GeometryCache::UNKNOWN_ID = -1; GeometryCache::GeometryCache() : _nextID(0) { - const qint64 GEOMETRY_DEFAULT_UNUSED_MAX_SIZE = DEFAULT_UNUSED_MAX_SIZE; - setUnusedResourceCacheSize(GEOMETRY_DEFAULT_UNUSED_MAX_SIZE); } GeometryCache::~GeometryCache() { @@ -51,13 +49,6 @@ GeometryCache::~GeometryCache() { #endif //def WANT_DEBUG } -QSharedPointer GeometryCache::createResource(const QUrl& url, const QSharedPointer& fallback, - bool delayLoad, const void* extra) { - // NetworkGeometry is no longer a subclass of Resource, but requires this method because, it is pure virtual. - assert(false); - return QSharedPointer(); -} - const int NUM_VERTICES_PER_TRIANGLE = 3; const int NUM_TRIANGLES_PER_QUAD = 2; const int NUM_VERTICES_PER_TRIANGULATED_QUAD = NUM_VERTICES_PER_TRIANGLE * NUM_TRIANGLES_PER_QUAD; @@ -1713,463 +1704,3 @@ void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend) { batch.setPipeline(_standardDrawPipeline); } } - -GeometryReader::GeometryReader(const QUrl& url, const QByteArray& data, const QVariantHash& mapping) : - _url(url), - _data(data), - _mapping(mapping) { -} - -void GeometryReader::run() { - try { - if (_data.isEmpty()) { - throw QString("Reply is NULL ?!"); - } - QString urlname = _url.path().toLower(); - bool urlValid = true; - urlValid &= !urlname.isEmpty(); - urlValid &= !_url.path().isEmpty(); - urlValid &= _url.path().toLower().endsWith(".fbx") || _url.path().toLower().endsWith(".obj"); - - if (urlValid) { - // Let's read the binaries from the network - FBXGeometry* fbxgeo = nullptr; - if (_url.path().toLower().endsWith(".fbx")) { - const bool grabLightmaps = true; - const float lightmapLevel = 1.0f; - fbxgeo = readFBX(_data, _mapping, _url.path(), grabLightmaps, lightmapLevel); - } else if (_url.path().toLower().endsWith(".obj")) { - fbxgeo = OBJReader().readOBJ(_data, _mapping, _url); - } else { - QString errorStr("usupported format"); - emit onError(NetworkGeometry::ModelParseError, errorStr); - } - emit onSuccess(fbxgeo); - } else { - throw QString("url is invalid"); - } - - } catch (const QString& error) { - qCDebug(renderutils) << "Error reading " << _url << ": " << error; - emit onError(NetworkGeometry::ModelParseError, error); - } -} - -NetworkGeometry::NetworkGeometry(const QUrl& url, bool delayLoad, const QVariantHash& mapping, const QUrl& textureBaseUrl) : - _url(url), - _mapping(mapping), - _textureBaseUrl(textureBaseUrl.isValid() ? textureBaseUrl : url) { - - if (delayLoad) { - _state = DelayState; - } else { - attemptRequestInternal(); - } -} - -NetworkGeometry::~NetworkGeometry() { - if (_resource) { - _resource->deleteLater(); - } -} - -void NetworkGeometry::attemptRequest() { - if (_state == DelayState) { - attemptRequestInternal(); - } -} - -void NetworkGeometry::attemptRequestInternal() { - if (_url.path().toLower().endsWith(".fst")) { - _mappingUrl = _url; - requestMapping(_url); - } else { - _modelUrl = _url; - requestModel(_url); - } -} - -bool NetworkGeometry::isLoaded() const { - return _state == SuccessState; -} - -bool NetworkGeometry::isLoadedWithTextures() const { - if (!isLoaded()) { - return false; - } - - if (!_isLoadedWithTextures) { - for (auto&& material : _materials) { - if ((material->diffuseTexture && !material->diffuseTexture->isLoaded()) || - (material->normalTexture && !material->normalTexture->isLoaded()) || - (material->specularTexture && !material->specularTexture->isLoaded()) || - (material->emissiveTexture && !material->emissiveTexture->isLoaded())) { - return false; - } - } - _isLoadedWithTextures = true; - } - return true; -} - -void NetworkGeometry::setTextureWithNameToURL(const QString& name, const QUrl& url) { - - - if (_meshes.size() > 0) { - auto textureCache = DependencyManager::get(); - for (auto&& material : _materials) { - QSharedPointer matchingTexture = QSharedPointer(); - if (material->diffuseTextureName == name) { - material->diffuseTexture = textureCache->getTexture(url, DEFAULT_TEXTURE); - } else if (material->normalTextureName == name) { - material->normalTexture = textureCache->getTexture(url); - } else if (material->specularTextureName == name) { - material->specularTexture = textureCache->getTexture(url); - } else if (material->emissiveTextureName == name) { - material->emissiveTexture = textureCache->getTexture(url); - } - } - } else { - qCWarning(renderutils) << "Ignoring setTextureWirthNameToURL() geometry not ready." << name << url; - } - _isLoadedWithTextures = false; -} - -QStringList NetworkGeometry::getTextureNames() const { - QStringList result; - for (auto&& material : _materials) { - if (!material->diffuseTextureName.isEmpty() && material->diffuseTexture) { - QString textureURL = material->diffuseTexture->getURL().toString(); - result << material->diffuseTextureName + ":" + textureURL; - } - - if (!material->normalTextureName.isEmpty() && material->normalTexture) { - QString textureURL = material->normalTexture->getURL().toString(); - result << material->normalTextureName + ":" + textureURL; - } - - if (!material->specularTextureName.isEmpty() && material->specularTexture) { - QString textureURL = material->specularTexture->getURL().toString(); - result << material->specularTextureName + ":" + textureURL; - } - - if (!material->emissiveTextureName.isEmpty() && material->emissiveTexture) { - QString textureURL = material->emissiveTexture->getURL().toString(); - result << material->emissiveTextureName + ":" + textureURL; - } - } - - return result; -} - -void NetworkGeometry::requestMapping(const QUrl& url) { - _state = RequestMappingState; - if (_resource) { - _resource->deleteLater(); - } - _resource = new Resource(url, false); - connect(_resource, &Resource::loaded, this, &NetworkGeometry::mappingRequestDone); - connect(_resource, &Resource::failed, this, &NetworkGeometry::mappingRequestError); -} - -void NetworkGeometry::requestModel(const QUrl& url) { - _state = RequestModelState; - if (_resource) { - _resource->deleteLater(); - } - _modelUrl = url; - _resource = new Resource(url, false); - connect(_resource, &Resource::loaded, this, &NetworkGeometry::modelRequestDone); - connect(_resource, &Resource::failed, this, &NetworkGeometry::modelRequestError); -} - -void NetworkGeometry::mappingRequestDone(const QByteArray& data) { - assert(_state == RequestMappingState); - - // parse the mapping file - _mapping = FSTReader::readMapping(data); - - QUrl replyUrl = _mappingUrl; - QString modelUrlStr = _mapping.value("filename").toString(); - if (modelUrlStr.isNull()) { - qCDebug(renderutils) << "Mapping file " << _url << "has no \"filename\" entry"; - emit onFailure(*this, MissingFilenameInMapping); - } else { - // read _textureBase from mapping file, if present - QString texdir = _mapping.value("texdir").toString(); - if (!texdir.isNull()) { - if (!texdir.endsWith('/')) { - texdir += '/'; - } - _textureBaseUrl = replyUrl.resolved(texdir); - } - - _modelUrl = replyUrl.resolved(modelUrlStr); - requestModel(_modelUrl); - } -} - -void NetworkGeometry::mappingRequestError(QNetworkReply::NetworkError error) { - assert(_state == RequestMappingState); - _state = ErrorState; - emit onFailure(*this, MappingRequestError); -} - -void NetworkGeometry::modelRequestDone(const QByteArray& data) { - assert(_state == RequestModelState); - - _state = ParsingModelState; - - // asynchronously parse the model file. - GeometryReader* geometryReader = new GeometryReader(_modelUrl, data, _mapping); - connect(geometryReader, SIGNAL(onSuccess(FBXGeometry*)), SLOT(modelParseSuccess(FBXGeometry*))); - connect(geometryReader, SIGNAL(onError(int, QString)), SLOT(modelParseError(int, QString))); - - QThreadPool::globalInstance()->start(geometryReader); -} - -void NetworkGeometry::modelRequestError(QNetworkReply::NetworkError error) { - assert(_state == RequestModelState); - _state = ErrorState; - emit onFailure(*this, ModelRequestError); -} - -static NetworkMesh* buildNetworkMesh(const FBXMesh& mesh, const QUrl& textureBaseUrl) { - auto textureCache = DependencyManager::get(); - NetworkMesh* networkMesh = new NetworkMesh(); - - int totalIndices = 0; - bool checkForTexcoordLightmap = false; - - - - // process network parts - foreach (const FBXMeshPart& part, mesh.parts) { - totalIndices += (part.quadIndices.size() + part.triangleIndices.size()); - } - - // initialize index buffer - { - networkMesh->_indexBuffer = std::make_shared(); - networkMesh->_indexBuffer->resize(totalIndices * sizeof(int)); - int offset = 0; - foreach(const FBXMeshPart& part, mesh.parts) { - networkMesh->_indexBuffer->setSubData(offset, part.quadIndices.size() * sizeof(int), - (gpu::Byte*) part.quadIndices.constData()); - offset += part.quadIndices.size() * sizeof(int); - networkMesh->_indexBuffer->setSubData(offset, part.triangleIndices.size() * sizeof(int), - (gpu::Byte*) part.triangleIndices.constData()); - offset += part.triangleIndices.size() * sizeof(int); - } - } - - // initialize vertex buffer - { - networkMesh->_vertexBuffer = std::make_shared(); - // if we don't need to do any blending, the positions/normals can be static - if (mesh.blendshapes.isEmpty()) { - int normalsOffset = mesh.vertices.size() * sizeof(glm::vec3); - int tangentsOffset = normalsOffset + mesh.normals.size() * sizeof(glm::vec3); - int colorsOffset = tangentsOffset + mesh.tangents.size() * sizeof(glm::vec3); - int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3); - int texCoords1Offset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2); - int clusterIndicesOffset = texCoords1Offset + mesh.texCoords1.size() * sizeof(glm::vec2); - int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4); - - networkMesh->_vertexBuffer->resize(clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4)); - - networkMesh->_vertexBuffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.vertices.constData()); - networkMesh->_vertexBuffer->setSubData(normalsOffset, mesh.normals.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.normals.constData()); - networkMesh->_vertexBuffer->setSubData(tangentsOffset, - mesh.tangents.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.tangents.constData()); - networkMesh->_vertexBuffer->setSubData(colorsOffset, mesh.colors.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.colors.constData()); - networkMesh->_vertexBuffer->setSubData(texCoordsOffset, - mesh.texCoords.size() * sizeof(glm::vec2), (gpu::Byte*) mesh.texCoords.constData()); - networkMesh->_vertexBuffer->setSubData(texCoords1Offset, - mesh.texCoords1.size() * sizeof(glm::vec2), (gpu::Byte*) mesh.texCoords1.constData()); - networkMesh->_vertexBuffer->setSubData(clusterIndicesOffset, - mesh.clusterIndices.size() * sizeof(glm::vec4), (gpu::Byte*) mesh.clusterIndices.constData()); - networkMesh->_vertexBuffer->setSubData(clusterWeightsOffset, - mesh.clusterWeights.size() * sizeof(glm::vec4), (gpu::Byte*) mesh.clusterWeights.constData()); - - // otherwise, at least the cluster indices/weights can be static - networkMesh->_vertexStream = std::make_shared(); - networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, 0, sizeof(glm::vec3)); - if (mesh.normals.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, normalsOffset, sizeof(glm::vec3)); - if (mesh.tangents.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, tangentsOffset, sizeof(glm::vec3)); - if (mesh.colors.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, colorsOffset, sizeof(glm::vec3)); - if (mesh.texCoords.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, texCoordsOffset, sizeof(glm::vec2)); - if (mesh.texCoords1.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, texCoords1Offset, sizeof(glm::vec2)); - if (mesh.clusterIndices.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, clusterIndicesOffset, sizeof(glm::vec4)); - if (mesh.clusterWeights.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, clusterWeightsOffset, sizeof(glm::vec4)); - - int channelNum = 0; - networkMesh->_vertexFormat = std::make_shared(); - networkMesh->_vertexFormat->setAttribute(gpu::Stream::POSITION, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); - if (mesh.normals.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::NORMAL, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); - if (mesh.tangents.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::TANGENT, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); - if (mesh.colors.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::COLOR, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RGB)); - if (mesh.texCoords.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::TEXCOORD, channelNum++, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); - if (mesh.texCoords1.size()) { - networkMesh->_vertexFormat->setAttribute(gpu::Stream::TEXCOORD1, channelNum++, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); - // } else if (checkForTexcoordLightmap && mesh.texCoords.size()) { - } else if (mesh.texCoords.size()) { - // need lightmap texcoord UV but doesn't have uv#1 so just reuse the same channel - networkMesh->_vertexFormat->setAttribute(gpu::Stream::TEXCOORD1, channelNum - 1, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); - } - if (mesh.clusterIndices.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_INDEX, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW)); - if (mesh.clusterWeights.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW)); - } - else { - int colorsOffset = mesh.tangents.size() * sizeof(glm::vec3); - int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3); - int clusterIndicesOffset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2); - int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4); - - networkMesh->_vertexBuffer->resize(clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4)); - networkMesh->_vertexBuffer->setSubData(0, mesh.tangents.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.tangents.constData()); - networkMesh->_vertexBuffer->setSubData(colorsOffset, mesh.colors.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.colors.constData()); - networkMesh->_vertexBuffer->setSubData(texCoordsOffset, - mesh.texCoords.size() * sizeof(glm::vec2), (gpu::Byte*) mesh.texCoords.constData()); - networkMesh->_vertexBuffer->setSubData(clusterIndicesOffset, - mesh.clusterIndices.size() * sizeof(glm::vec4), (gpu::Byte*) mesh.clusterIndices.constData()); - networkMesh->_vertexBuffer->setSubData(clusterWeightsOffset, - mesh.clusterWeights.size() * sizeof(glm::vec4), (gpu::Byte*) mesh.clusterWeights.constData()); - - networkMesh->_vertexStream = std::make_shared(); - if (mesh.tangents.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, 0, sizeof(glm::vec3)); - if (mesh.colors.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, colorsOffset, sizeof(glm::vec3)); - if (mesh.texCoords.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, texCoordsOffset, sizeof(glm::vec2)); - if (mesh.clusterIndices.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, clusterIndicesOffset, sizeof(glm::vec4)); - if (mesh.clusterWeights.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, clusterWeightsOffset, sizeof(glm::vec4)); - - int channelNum = 0; - networkMesh->_vertexFormat = std::make_shared(); - networkMesh->_vertexFormat->setAttribute(gpu::Stream::POSITION, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); - if (mesh.normals.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::NORMAL, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); - if (mesh.tangents.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::TANGENT, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); - if (mesh.colors.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::COLOR, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RGB)); - if (mesh.texCoords.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::TEXCOORD, channelNum++, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); - if (mesh.clusterIndices.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_INDEX, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW)); - if (mesh.clusterWeights.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW)); - } - } - - return networkMesh; -} - -static NetworkMaterial* buildNetworkMaterial(const FBXMaterial& material, const QUrl& textureBaseUrl) { - auto textureCache = DependencyManager::get(); - NetworkMaterial* networkMaterial = new NetworkMaterial(); - - int totalIndices = 0; - bool checkForTexcoordLightmap = false; - - networkMaterial->_material = material._material; - - if (!material.diffuseTexture.filename.isEmpty()) { - networkMaterial->diffuseTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(material.diffuseTexture.filename)), DEFAULT_TEXTURE, material.diffuseTexture.content); - networkMaterial->diffuseTextureName = material.diffuseTexture.name; - - auto diffuseMap = model::TextureMapPointer(new model::TextureMap()); - diffuseMap->setTextureSource(networkMaterial->diffuseTexture->_textureSource); - diffuseMap->setTextureTransform(material.diffuseTexture.transform); - - material._material->setTextureMap(model::MaterialKey::DIFFUSE_MAP, diffuseMap); - } - if (!material.normalTexture.filename.isEmpty()) { - networkMaterial->normalTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(material.normalTexture.filename)), (material.normalTexture.isBumpmap ? BUMP_TEXTURE : NORMAL_TEXTURE), material.normalTexture.content); - networkMaterial->normalTextureName = material.normalTexture.name; - - auto normalMap = model::TextureMapPointer(new model::TextureMap()); - normalMap->setTextureSource(networkMaterial->normalTexture->_textureSource); - - material._material->setTextureMap(model::MaterialKey::NORMAL_MAP, normalMap); - } - if (!material.specularTexture.filename.isEmpty()) { - networkMaterial->specularTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(material.specularTexture.filename)), SPECULAR_TEXTURE, material.specularTexture.content); - networkMaterial->specularTextureName = material.specularTexture.name; - - auto glossMap = model::TextureMapPointer(new model::TextureMap()); - glossMap->setTextureSource(networkMaterial->specularTexture->_textureSource); - - material._material->setTextureMap(model::MaterialKey::GLOSS_MAP, glossMap); - } - if (!material.emissiveTexture.filename.isEmpty()) { - networkMaterial->emissiveTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(material.emissiveTexture.filename)), EMISSIVE_TEXTURE, material.emissiveTexture.content); - networkMaterial->emissiveTextureName = material.emissiveTexture.name; - - checkForTexcoordLightmap = true; - - auto lightmapMap = model::TextureMapPointer(new model::TextureMap()); - lightmapMap->setTextureSource(networkMaterial->emissiveTexture->_textureSource); - lightmapMap->setTextureTransform(material.emissiveTexture.transform); - lightmapMap->setLightmapOffsetScale(material.emissiveParams.x, material.emissiveParams.y); - - material._material->setTextureMap(model::MaterialKey::LIGHTMAP_MAP, lightmapMap); - } - - return networkMaterial; -} - - -void NetworkGeometry::modelParseSuccess(FBXGeometry* geometry) { - // assume owner ship of geometry pointer - _geometry.reset(geometry); - - - - foreach(const FBXMesh& mesh, _geometry->meshes) { - _meshes.emplace_back(buildNetworkMesh(mesh, _textureBaseUrl)); - } - - QHash fbxMatIDToMatID; - foreach(const FBXMaterial& material, _geometry->materials) { - fbxMatIDToMatID[material.materialID] = _materials.size(); - _materials.emplace_back(buildNetworkMaterial(material, _textureBaseUrl)); - } - - - int meshID = 0; - foreach(const FBXMesh& mesh, _geometry->meshes) { - int partID = 0; - foreach (const FBXMeshPart& part, mesh.parts) { - NetworkShape* networkShape = new NetworkShape(); - networkShape->_meshID = meshID; - networkShape->_partID = partID; - networkShape->_materialID = fbxMatIDToMatID[part.materialID]; - _shapes.emplace_back(networkShape); - partID++; - } - meshID++; - } - - _state = SuccessState; - emit onSuccess(*this, *_geometry.get()); - - delete _resource; - _resource = nullptr; -} - -void NetworkGeometry::modelParseError(int error, QString str) { - _state = ErrorState; - emit onFailure(*this, (NetworkGeometry::Error)error); - - delete _resource; - _resource = nullptr; -} - - -const NetworkMaterial* NetworkGeometry::getShapeMaterial(int shapeID) { - if ((shapeID >= 0) && (shapeID < _shapes.size())) { - int materialID = _shapes[shapeID]->_materialID; - if ((materialID >= 0) && (materialID < _materials.size())) { - return _materials[materialID].get(); - } else { - return 0; - } - } else { - return 0; - } -} - diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index a411d83c18..6102489b5c 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -12,14 +12,12 @@ #ifndef hifi_GeometryCache_h #define hifi_GeometryCache_h +#include "model-networking/ModelCache.h" + #include #include #include -#include - -#include "FBXReader.h" -#include "OBJReader.h" #include #include @@ -28,13 +26,6 @@ #include #include -class NetworkGeometry; -class NetworkMesh; -class NetworkTexture; -class NetworkMaterial; -class NetworkShape; - - typedef glm::vec3 Vec3Key; typedef QPair Vec2Pair; @@ -125,17 +116,13 @@ inline uint qHash(const Vec4PairVec4Pair& v, uint seed) { } /// Stores cached geometry. -class GeometryCache : public ResourceCache, public Dependency { - Q_OBJECT +class GeometryCache : public Dependency { SINGLETON_DEPENDENCY public: int allocateID() { return _nextID++; } static const int UNKNOWN_ID; - virtual QSharedPointer createResource(const QUrl& url, const QSharedPointer& fallback, - bool delayLoad, const void* extra); - gpu::BufferPointer getCubeVertices(float size); void setupCubeVertices(gpu::Batch& batch, gpu::BufferPointer& verticesBuffer); @@ -313,152 +300,6 @@ private: QHash _sphereColors; QHash _registeredSphereColors; QHash _lastRegisteredSphereColors; - - QHash > _networkGeometry; -}; - -class NetworkGeometry : public QObject { - Q_OBJECT - -public: - // mapping is only used if url is a .fbx or .obj file, it is essentially the content of an fst file. - // if delayLoad is true, the url will not be immediately downloaded. - // use the attemptRequest method to initiate the download. - NetworkGeometry(const QUrl& url, bool delayLoad, const QVariantHash& mapping, const QUrl& textureBaseUrl = QUrl()); - ~NetworkGeometry(); - - const QUrl& getURL() const { return _url; } - - void attemptRequest(); - - // true when the geometry is loaded (but maybe not it's associated textures) - bool isLoaded() const; - - // true when the requested geometry and its textures are loaded. - bool isLoadedWithTextures() const; - - // WARNING: only valid when isLoaded returns true. - const FBXGeometry& getFBXGeometry() const { return *_geometry; } - const std::vector>& getMeshes() const { return _meshes; } - // const model::AssetPointer getAsset() const { return _asset; } - - // model::MeshPointer getShapeMesh(int shapeID); - // int getShapePart(int shapeID); - - // This would be the final verison - // model::MaterialPointer getShapeMaterial(int shapeID); - const NetworkMaterial* getShapeMaterial(int shapeID); - - - void setTextureWithNameToURL(const QString& name, const QUrl& url); - QStringList getTextureNames() const; - - enum Error { - MissingFilenameInMapping = 0, - MappingRequestError, - ModelRequestError, - ModelParseError - }; - -signals: - // Fired when everything has downloaded and parsed successfully. - void onSuccess(NetworkGeometry& networkGeometry, FBXGeometry& fbxGeometry); - - // Fired when something went wrong. - void onFailure(NetworkGeometry& networkGeometry, Error error); - -protected slots: - void mappingRequestDone(const QByteArray& data); - void mappingRequestError(QNetworkReply::NetworkError error); - - void modelRequestDone(const QByteArray& data); - void modelRequestError(QNetworkReply::NetworkError error); - - void modelParseSuccess(FBXGeometry* geometry); - void modelParseError(int error, QString str); - -protected: - void attemptRequestInternal(); - void requestMapping(const QUrl& url); - void requestModel(const QUrl& url); - - enum State { DelayState, - RequestMappingState, - RequestModelState, - ParsingModelState, - SuccessState, - ErrorState }; - State _state; - - QUrl _url; - QUrl _mappingUrl; - QUrl _modelUrl; - QVariantHash _mapping; - QUrl _textureBaseUrl; - - Resource* _resource = nullptr; - std::unique_ptr _geometry; // This should go away evenutally once we can put everything we need in the model::AssetPointer - std::vector> _meshes; - std::vector> _materials; - std::vector> _shapes; - - - // The model asset created from this NetworkGeometry - // model::AssetPointer _asset; - - // cache for isLoadedWithTextures() - mutable bool _isLoadedWithTextures = false; -}; - -/// Reads geometry in a worker thread. -class GeometryReader : public QObject, public QRunnable { - Q_OBJECT -public: - GeometryReader(const QUrl& url, const QByteArray& data, const QVariantHash& mapping); - virtual void run(); -signals: - void onSuccess(FBXGeometry* geometry); - void onError(int error, QString str); -private: - QUrl _url; - QByteArray _data; - QVariantHash _mapping; -}; - - -class NetworkShape { -public: - int _meshID{ -1 }; - int _partID{ -1 }; - int _materialID{ -1 }; -}; - -class NetworkMaterial { -public: - model::MaterialPointer _material; - QString diffuseTextureName; - QSharedPointer diffuseTexture; - QString normalTextureName; - QSharedPointer normalTexture; - QString specularTextureName; - QSharedPointer specularTexture; - QString emissiveTextureName; - QSharedPointer emissiveTexture; -}; - - -/// The state associated with a single mesh. -class NetworkMesh { -public: - gpu::BufferPointer _indexBuffer; - gpu::BufferPointer _vertexBuffer; - - gpu::BufferStreamPointer _vertexStream; - - gpu::Stream::FormatPointer _vertexFormat; - - int getTranslucentPartCount(const FBXMesh& fbxMesh) const; - bool isPartTranslucent(const FBXMesh& fbxMesh, int partIndex) const; }; #endif // hifi_GeometryCache_h diff --git a/libraries/render-utils/src/TextureCache.h b/libraries/render-utils/src/TextureCache.h index 23ac11d7b0..d6c1e419b9 100644 --- a/libraries/render-utils/src/TextureCache.h +++ b/libraries/render-utils/src/TextureCache.h @@ -1,2 +1,2 @@ // Compatibility -#include +#include diff --git a/libraries/script-engine/CMakeLists.txt b/libraries/script-engine/CMakeLists.txt index 1acfb57829..a458d4de51 100644 --- a/libraries/script-engine/CMakeLists.txt +++ b/libraries/script-engine/CMakeLists.txt @@ -7,4 +7,4 @@ add_dependency_external_projects(glm) find_package(GLM REQUIRED) target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS}) -link_hifi_libraries(shared networking octree gpu gpu-networking procedural model fbx entities animation audio physics) +link_hifi_libraries(shared networking octree gpu procedural model model-networking fbx entities animation audio physics) diff --git a/tests/gpu-test/CMakeLists.txt b/tests/gpu-test/CMakeLists.txt index 3d42364132..1cb9e9f78e 100644 --- a/tests/gpu-test/CMakeLists.txt +++ b/tests/gpu-test/CMakeLists.txt @@ -10,6 +10,6 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") #include_oglplus() # link in the shared libraries -link_hifi_libraries(networking gpu gpu-networking procedural shared fbx model animation script-engine render-utils ) +link_hifi_libraries(networking gpu procedural shared fbx model model-networking animation script-engine render-utils ) copy_dlls_beside_windows_executable() \ No newline at end of file