mirror of
https://github.com/overte-org/overte.git
synced 2025-04-14 07:47:30 +02:00
Redistributing the files to create the model-networking lib and separate that from the redner-utils
This commit is contained in:
parent
2875cb99bb
commit
d3b1bcb86d
36 changed files with 1055 additions and 748 deletions
|
@ -113,7 +113,7 @@ endif()
|
||||||
target_link_libraries(${TARGET_NAME} ${BULLET_LIBRARIES})
|
target_link_libraries(${TARGET_NAME} ${BULLET_LIBRARIES})
|
||||||
|
|
||||||
# link required hifi 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
|
audio audio-client animation script-engine physics
|
||||||
render-utils entities-renderer ui auto-updater
|
render-utils entities-renderer ui auto-updater
|
||||||
plugins display-plugins input-plugins)
|
plugins display-plugins input-plugins)
|
||||||
|
|
|
@ -278,6 +278,7 @@ bool setupEssentials(int& argc, char** argv) {
|
||||||
auto addressManager = DependencyManager::set<AddressManager>();
|
auto addressManager = DependencyManager::set<AddressManager>();
|
||||||
auto nodeList = DependencyManager::set<NodeList>(NodeType::Agent, listenPort);
|
auto nodeList = DependencyManager::set<NodeList>(NodeType::Agent, listenPort);
|
||||||
auto geometryCache = DependencyManager::set<GeometryCache>();
|
auto geometryCache = DependencyManager::set<GeometryCache>();
|
||||||
|
auto modelCache = DependencyManager::set<ModelCache>();
|
||||||
auto scriptCache = DependencyManager::set<ScriptCache>();
|
auto scriptCache = DependencyManager::set<ScriptCache>();
|
||||||
auto soundCache = DependencyManager::set<SoundCache>();
|
auto soundCache = DependencyManager::set<SoundCache>();
|
||||||
auto faceshift = DependencyManager::set<Faceshift>();
|
auto faceshift = DependencyManager::set<Faceshift>();
|
||||||
|
@ -418,12 +419,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
||||||
// put the NodeList and datagram processing on the node thread
|
// put the NodeList and datagram processing on the node thread
|
||||||
nodeList->moveToThread(nodeThread);
|
nodeList->moveToThread(nodeThread);
|
||||||
|
|
||||||
// geometry background downloads need to happen on the Datagram Processor Thread. The idle loop will
|
// Model 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
|
// emit checkBackgroundDownloads to cause the ModelCache to check it's queue for requested background
|
||||||
// downloads.
|
// downloads.
|
||||||
QSharedPointer<GeometryCache> geometryCacheP = DependencyManager::get<GeometryCache>();
|
QSharedPointer<ModelCache> modelCacheP = DependencyManager::get<ModelCache>();
|
||||||
ResourceCache* geometryCache = geometryCacheP.data();
|
ResourceCache* modelCache = modelCacheP.data();
|
||||||
connect(this, &Application::checkBackgroundDownloads, geometryCache, &ResourceCache::checkAsynchronousGets);
|
connect(this, &Application::checkBackgroundDownloads, modelCache, &ResourceCache::checkAsynchronousGets);
|
||||||
|
|
||||||
// put the audio processing on a separate thread
|
// put the audio processing on a separate thread
|
||||||
QThread* audioThread = new QThread();
|
QThread* audioThread = new QThread();
|
||||||
|
@ -892,6 +893,7 @@ Application::~Application() {
|
||||||
DependencyManager::destroy<AnimationCache>();
|
DependencyManager::destroy<AnimationCache>();
|
||||||
DependencyManager::destroy<FramebufferCache>();
|
DependencyManager::destroy<FramebufferCache>();
|
||||||
DependencyManager::destroy<TextureCache>();
|
DependencyManager::destroy<TextureCache>();
|
||||||
|
DependencyManager::destroy<ModelCache>();
|
||||||
DependencyManager::destroy<GeometryCache>();
|
DependencyManager::destroy<GeometryCache>();
|
||||||
DependencyManager::destroy<ScriptCache>();
|
DependencyManager::destroy<ScriptCache>();
|
||||||
DependencyManager::destroy<SoundCache>();
|
DependencyManager::destroy<SoundCache>();
|
||||||
|
@ -2718,7 +2720,7 @@ void Application::reloadResourceCaches() {
|
||||||
emptyLocalCache();
|
emptyLocalCache();
|
||||||
|
|
||||||
DependencyManager::get<AnimationCache>()->refreshAll();
|
DependencyManager::get<AnimationCache>()->refreshAll();
|
||||||
DependencyManager::get<GeometryCache>()->refreshAll();
|
DependencyManager::get<ModelCache>()->refreshAll();
|
||||||
DependencyManager::get<SoundCache>()->refreshAll();
|
DependencyManager::get<SoundCache>()->refreshAll();
|
||||||
DependencyManager::get<TextureCache>()->refreshAll();
|
DependencyManager::get<TextureCache>()->refreshAll();
|
||||||
}
|
}
|
||||||
|
|
|
@ -188,8 +188,10 @@ void Stars::render(RenderArgs* renderArgs, float alpha) {
|
||||||
colorElement = streamFormat->getAttributes().at(gpu::Stream::COLOR)._element;
|
colorElement = streamFormat->getAttributes().at(gpu::Stream::COLOR)._element;
|
||||||
});
|
});
|
||||||
|
|
||||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
auto modelCache = DependencyManager::get<ModelCache>();
|
||||||
auto textureCache = DependencyManager::get<TextureCache>();
|
auto textureCache = DependencyManager::get<TextureCache>();
|
||||||
|
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||||
|
|
||||||
|
|
||||||
gpu::Batch& batch = *renderArgs->_batch;
|
gpu::Batch& batch = *renderArgs->_batch;
|
||||||
batch.setViewTransform(Transform());
|
batch.setViewTransform(Transform());
|
||||||
|
|
|
@ -56,7 +56,7 @@ CachesSizeDialog::CachesSizeDialog(QWidget* parent) :
|
||||||
|
|
||||||
void CachesSizeDialog::confirmClicked(bool checked) {
|
void CachesSizeDialog::confirmClicked(bool checked) {
|
||||||
DependencyManager::get<AnimationCache>()->setUnusedResourceCacheSize(_animations->value() * BYTES_PER_MEGABYTES);
|
DependencyManager::get<AnimationCache>()->setUnusedResourceCacheSize(_animations->value() * BYTES_PER_MEGABYTES);
|
||||||
DependencyManager::get<GeometryCache>()->setUnusedResourceCacheSize(_geometries->value() * BYTES_PER_MEGABYTES);
|
DependencyManager::get<ModelCache>()->setUnusedResourceCacheSize(_geometries->value() * BYTES_PER_MEGABYTES);
|
||||||
DependencyManager::get<SoundCache>()->setUnusedResourceCacheSize(_sounds->value() * BYTES_PER_MEGABYTES);
|
DependencyManager::get<SoundCache>()->setUnusedResourceCacheSize(_sounds->value() * BYTES_PER_MEGABYTES);
|
||||||
DependencyManager::get<TextureCache>()->setUnusedResourceCacheSize(_textures->value() * BYTES_PER_MEGABYTES);
|
DependencyManager::get<TextureCache>()->setUnusedResourceCacheSize(_textures->value() * BYTES_PER_MEGABYTES);
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ void CachesSizeDialog::confirmClicked(bool checked) {
|
||||||
|
|
||||||
void CachesSizeDialog::resetClicked(bool checked) {
|
void CachesSizeDialog::resetClicked(bool checked) {
|
||||||
_animations->setValue(DependencyManager::get<AnimationCache>()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES);
|
_animations->setValue(DependencyManager::get<AnimationCache>()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES);
|
||||||
_geometries->setValue(DependencyManager::get<GeometryCache>()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES);
|
_geometries->setValue(DependencyManager::get<ModelCache>()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES);
|
||||||
_sounds->setValue(DependencyManager::get<SoundCache>()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES);
|
_sounds->setValue(DependencyManager::get<SoundCache>()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES);
|
||||||
_textures->setValue(DependencyManager::get<TextureCache>()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES);
|
_textures->setValue(DependencyManager::get<TextureCache>()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,4 +26,4 @@ find_package(PolyVox REQUIRED)
|
||||||
target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${POLYVOX_INCLUDE_DIRS})
|
target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${POLYVOX_INCLUDE_DIRS})
|
||||||
target_link_libraries(${TARGET_NAME} ${POLYVOX_LIBRARIES})
|
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)
|
||||||
|
|
|
@ -71,3 +71,28 @@ bool Shader::makeProgram(Shader& shader, const Shader::BindingSet& bindings) {
|
||||||
}
|
}
|
||||||
return false;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
namespace gpu {
|
namespace gpu {
|
||||||
|
|
||||||
|
@ -187,6 +189,27 @@ protected:
|
||||||
typedef Shader::Pointer ShaderPointer;
|
typedef Shader::Pointer ShaderPointer;
|
||||||
typedef std::vector< ShaderPointer > Shaders;
|
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;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -768,3 +768,28 @@ void SphericalHarmonics::evalFromTexture(const Texture& texture) {
|
||||||
L22 = coefs[8];
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
|
|
||||||
#include <algorithm> //min max and more
|
#include <algorithm> //min max and more
|
||||||
|
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
namespace gpu {
|
namespace gpu {
|
||||||
|
|
||||||
// THe spherical harmonics is a nice tool for cubemap, so if required, the irradiance SH can be automatically generated
|
// 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<TextureView> TextureViews;
|
typedef std::vector<TextureView> 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;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
||||||
setup_hifi_library()
|
setup_hifi_library()
|
506
libraries/model-networking/src/model-networking/ModelCache.cpp
Normal file
506
libraries/model-networking/src/model-networking/ModelCache.cpp
Normal file
|
@ -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 <cmath>
|
||||||
|
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QThreadPool>
|
||||||
|
|
||||||
|
#include <FSTReader.h>
|
||||||
|
#include <NumericalConstants.h>
|
||||||
|
|
||||||
|
#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<Resource> ModelCache::createResource(const QUrl& url, const QSharedPointer<Resource>& 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<Resource>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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<TextureCache>();
|
||||||
|
for (auto&& material : _materials) {
|
||||||
|
QSharedPointer<NetworkTexture> matchingTexture = QSharedPointer<NetworkTexture>();
|
||||||
|
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<TextureCache>();
|
||||||
|
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<gpu::Buffer>();
|
||||||
|
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<gpu::Buffer>();
|
||||||
|
// 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<gpu::BufferStream>();
|
||||||
|
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<gpu::Stream::Format>();
|
||||||
|
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<gpu::BufferStream>();
|
||||||
|
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<gpu::Stream::Format>();
|
||||||
|
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<TextureCache>();
|
||||||
|
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<QString, int> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
205
libraries/model-networking/src/model-networking/ModelCache.h
Normal file
205
libraries/model-networking/src/model-networking/ModelCache.h
Normal file
|
@ -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 <QMap>
|
||||||
|
#include <QRunnable>
|
||||||
|
|
||||||
|
#include <DependencyManager.h>
|
||||||
|
#include <ResourceCache.h>
|
||||||
|
|
||||||
|
#include "FBXReader.h"
|
||||||
|
#include "OBJReader.h"
|
||||||
|
|
||||||
|
#include <gpu/Batch.h>
|
||||||
|
#include <gpu/Stream.h>
|
||||||
|
|
||||||
|
|
||||||
|
#include <model/Material.h>
|
||||||
|
#include <model/Asset.h>
|
||||||
|
|
||||||
|
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<Resource> createResource(const QUrl& url, const QSharedPointer<Resource>& 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<NetworkGeometry> 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<QUrl, QWeakPointer<NetworkGeometry> > _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<std::unique_ptr<NetworkMesh>>& 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<FBXGeometry> _geometry; // This should go away evenutally once we can put everything we need in the model::AssetPointer
|
||||||
|
std::vector<std::unique_ptr<NetworkMesh>> _meshes;
|
||||||
|
std::vector<std::unique_ptr<NetworkMaterial>> _materials;
|
||||||
|
std::vector<std::unique_ptr<NetworkShape>> _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<NetworkTexture> diffuseTexture;
|
||||||
|
QString normalTextureName;
|
||||||
|
QSharedPointer<NetworkTexture> normalTexture;
|
||||||
|
QString specularTextureName;
|
||||||
|
QSharedPointer<NetworkTexture> specularTexture;
|
||||||
|
QString emissiveTextureName;
|
||||||
|
QSharedPointer<NetworkTexture> 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
|
|
@ -6,6 +6,6 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// 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")
|
|
@ -8,4 +8,4 @@
|
||||||
|
|
||||||
#include <QLoggingCategory>
|
#include <QLoggingCategory>
|
||||||
|
|
||||||
Q_DECLARE_LOGGING_CATEGORY(gpunetwork)
|
Q_DECLARE_LOGGING_CATEGORY(modelnetworking)
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// TextureCache.cpp
|
// TextureCache.cpp
|
||||||
// libraries/gpu-networking/src
|
// libraries/model-networking/src
|
||||||
//
|
//
|
||||||
// Created by Andrzej Kapolka on 8/6/13.
|
// Created by Andrzej Kapolka on 8/6/13.
|
||||||
// Copyright 2013 High Fidelity, Inc.
|
// Copyright 2013 High Fidelity, Inc.
|
||||||
|
@ -25,7 +25,7 @@
|
||||||
|
|
||||||
#include <gpu/Batch.h>
|
#include <gpu/Batch.h>
|
||||||
|
|
||||||
#include "GpuNetworkingLogging.h"
|
#include "ModelNetworkingLogging.h"
|
||||||
|
|
||||||
TextureCache::TextureCache() {
|
TextureCache::TextureCache() {
|
||||||
const qint64 TEXTURE_DEFAULT_UNUSED_MAX_SIZE = DEFAULT_UNUSED_MAX_SIZE;
|
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),
|
_width(0),
|
||||||
_height(0) {
|
_height(0) {
|
||||||
|
|
||||||
_textureSource.reset(new model::TextureSource());
|
_textureSource.reset(new gpu::TextureSource());
|
||||||
|
|
||||||
if (!url.isValid()) {
|
if (!url.isValid()) {
|
||||||
_loaded = true;
|
_loaded = true;
|
||||||
|
@ -206,7 +206,7 @@ NetworkTexture::NetworkTexture(const QUrl& url, const TextureLoaderFunc& texture
|
||||||
_width(0),
|
_width(0),
|
||||||
_height(0) {
|
_height(0) {
|
||||||
|
|
||||||
_textureSource.reset(new model::TextureSource());
|
_textureSource.reset(new gpu::TextureSource());
|
||||||
|
|
||||||
if (!url.isValid()) {
|
if (!url.isValid()) {
|
||||||
_loaded = true;
|
_loaded = true;
|
||||||
|
@ -223,11 +223,11 @@ NetworkTexture::NetworkTexture(const QUrl& url, const TextureLoaderFunc& texture
|
||||||
NetworkTexture::TextureLoaderFunc NetworkTexture::getTextureLoader() const {
|
NetworkTexture::TextureLoaderFunc NetworkTexture::getTextureLoader() const {
|
||||||
switch (_type) {
|
switch (_type) {
|
||||||
case CUBE_TEXTURE: {
|
case CUBE_TEXTURE: {
|
||||||
return TextureLoaderFunc(model::TextureSource::createCubeTextureFromImage);
|
return TextureLoaderFunc(model::TextureUsage::createCubeTextureFromImage);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case BUMP_TEXTURE: {
|
case BUMP_TEXTURE: {
|
||||||
return TextureLoaderFunc(model::TextureSource::createNormalTextureFromBumpImage);
|
return TextureLoaderFunc(model::TextureUsage::createNormalTextureFromBumpImage);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CUSTOM_TEXTURE: {
|
case CUSTOM_TEXTURE: {
|
||||||
|
@ -239,7 +239,7 @@ NetworkTexture::TextureLoaderFunc NetworkTexture::getTextureLoader() const {
|
||||||
case SPECULAR_TEXTURE:
|
case SPECULAR_TEXTURE:
|
||||||
case EMISSIVE_TEXTURE:
|
case EMISSIVE_TEXTURE:
|
||||||
default: {
|
default: {
|
||||||
return TextureLoaderFunc(model::TextureSource::create2DTextureFromImage);
|
return TextureLoaderFunc(model::TextureUsage::create2DTextureFromImage);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -288,7 +288,7 @@ void listSupportedImageFormats() {
|
||||||
foreach(const QByteArray& f, supportedFormats) {
|
foreach(const QByteArray& f, supportedFormats) {
|
||||||
formats += QString(f) + ",";
|
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 (originalWidth == 0 || originalHeight == 0 || imageFormat == QImage::Format_Invalid) {
|
||||||
if (filenameExtension.empty()) {
|
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 {
|
} else {
|
||||||
qCDebug(gpunetwork) << "QImage failed to create from content" << _url;
|
qCDebug(modelnetworking) << "QImage failed to create from content" << _url;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// TextureCache.h
|
// TextureCache.h
|
||||||
// libraries/gpu-networking/src
|
// libraries/model-networking/src
|
||||||
//
|
//
|
||||||
// Created by Andrzej Kapolka on 8/6/13.
|
// Created by Andrzej Kapolka on 8/6/13.
|
||||||
// Copyright 2013 High Fidelity, Inc.
|
// Copyright 2013 High Fidelity, Inc.
|
||||||
|
@ -98,7 +98,7 @@ public:
|
||||||
~Texture();
|
~Texture();
|
||||||
|
|
||||||
const gpu::TexturePointer getGPUTexture() const { return _textureSource->getGPUTexture(); }
|
const gpu::TexturePointer getGPUTexture() const { return _textureSource->getGPUTexture(); }
|
||||||
model::TextureSourcePointer _textureSource;
|
gpu::TextureSourcePointer _textureSource;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -9,4 +9,4 @@ add_dependency_external_projects(glm)
|
||||||
find_package(GLM REQUIRED)
|
find_package(GLM REQUIRED)
|
||||||
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
|
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
|
||||||
|
|
||||||
link_hifi_libraries(shared networking gpu octree)
|
link_hifi_libraries(shared gpu)
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
|
|
||||||
#include <gpu/Batch.h>
|
#include <gpu/Batch.h>
|
||||||
#include <gpu/Context.h>
|
#include <gpu/Context.h>
|
||||||
// #include <procedural/Procedural.h>
|
|
||||||
#include <ViewFrustum.h>
|
#include <ViewFrustum.h>
|
||||||
|
|
||||||
#include "Skybox_vert.h"
|
#include "Skybox_vert.h"
|
||||||
|
@ -39,16 +38,7 @@ Skybox::Skybox() {
|
||||||
void Skybox::setColor(const Color& color) {
|
void Skybox::setColor(const Color& color) {
|
||||||
_color = color;
|
_color = color;
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
void Skybox::setProcedural(QSharedPointer<Procedural> procedural) {
|
|
||||||
_procedural = procedural;
|
|
||||||
if (_procedural) {
|
|
||||||
_procedural->_vertexSource = Skybox_vert;
|
|
||||||
_procedural->_fragmentSource = Skybox_frag;
|
|
||||||
// No pipeline state customization
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
void Skybox::setCubemap(const gpu::TexturePointer& cubemap) {
|
void Skybox::setCubemap(const gpu::TexturePointer& cubemap) {
|
||||||
_cubemap = cubemap;
|
_cubemap = cubemap;
|
||||||
}
|
}
|
||||||
|
@ -58,7 +48,7 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky
|
||||||
static gpu::BufferPointer theBuffer;
|
static gpu::BufferPointer theBuffer;
|
||||||
static gpu::Stream::FormatPointer theFormat;
|
static gpu::Stream::FormatPointer theFormat;
|
||||||
|
|
||||||
if (/*skybox._procedural || */skybox.getCubemap()) {
|
if (skybox._procedural || skybox.getCubemap()) {
|
||||||
if (!theBuffer) {
|
if (!theBuffer) {
|
||||||
const float CLIP = 1.0f;
|
const float CLIP = 1.0f;
|
||||||
const glm::vec2 vertices[4] = { { -CLIP, -CLIP }, { CLIP, -CLIP }, { -CLIP, CLIP }, { CLIP, CLIP } };
|
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.setInputBuffer(gpu::Stream::POSITION, theBuffer, 0, 8);
|
||||||
batch.setInputFormat(theFormat);
|
batch.setInputFormat(theFormat);
|
||||||
|
|
||||||
/*if (skybox._procedural && skybox._procedural->_enabled && skybox._procedural->ready()) {
|
if (skybox.getCubemap() && skybox.getCubemap()->isDefined()) {
|
||||||
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()) {
|
|
||||||
static gpu::BufferPointer theConstants;
|
static gpu::BufferPointer theConstants;
|
||||||
static gpu::PipelinePointer thePipeline;
|
static gpu::PipelinePointer thePipeline;
|
||||||
static int SKYBOX_CONSTANTS_SLOT = 0; // need to be defined by the compilation of the shader
|
static int SKYBOX_CONSTANTS_SLOT = 0; // need to be defined by the compilation of the shader
|
||||||
|
|
|
@ -11,13 +11,14 @@
|
||||||
#ifndef hifi_model_Skybox_h
|
#ifndef hifi_model_Skybox_h
|
||||||
#define hifi_model_Skybox_h
|
#define hifi_model_Skybox_h
|
||||||
|
|
||||||
//#include <QtCore/QSharedPointer>
|
|
||||||
#include <gpu/Texture.h>
|
#include <gpu/Texture.h>
|
||||||
|
|
||||||
#include "Light.h"
|
#include "Light.h"
|
||||||
|
|
||||||
class ViewFrustum;
|
class ViewFrustum;
|
||||||
struct Procedural;
|
struct Procedural;
|
||||||
|
typedef std::shared_ptr<Procedural> ProceduralPointer;
|
||||||
|
|
||||||
namespace gpu { class Batch; }
|
namespace gpu { class Batch; }
|
||||||
|
|
||||||
namespace model {
|
namespace model {
|
||||||
|
@ -36,13 +37,13 @@ public:
|
||||||
void setCubemap(const gpu::TexturePointer& cubemap);
|
void setCubemap(const gpu::TexturePointer& cubemap);
|
||||||
const gpu::TexturePointer& getCubemap() const { return _cubemap; }
|
const gpu::TexturePointer& getCubemap() const { return _cubemap; }
|
||||||
|
|
||||||
// void setProcedural(QSharedPointer<Procedural> procedural);
|
void setProcedural(const ProceduralPointer& procedural);
|
||||||
|
|
||||||
static void render(gpu::Batch& batch, const ViewFrustum& frustum, const Skybox& skybox);
|
static void render(gpu::Batch& batch, const ViewFrustum& frustum, const Skybox& skybox);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
gpu::TexturePointer _cubemap;
|
gpu::TexturePointer _cubemap;
|
||||||
// QSharedPointer<Procedural> _procedural;
|
ProceduralPointer _procedural;
|
||||||
Color _color{1.0f, 1.0f, 1.0f};
|
Color _color{1.0f, 1.0f, 1.0f};
|
||||||
};
|
};
|
||||||
typedef std::shared_ptr< Skybox > SkyboxPointer;
|
typedef std::shared_ptr< Skybox > SkyboxPointer;
|
||||||
|
|
|
@ -19,35 +19,9 @@
|
||||||
using namespace model;
|
using namespace model;
|
||||||
using namespace gpu;
|
using namespace gpu;
|
||||||
|
|
||||||
// TextureSource
|
|
||||||
TextureSource::TextureSource()
|
|
||||||
{/* : Texture::Storage()//,
|
|
||||||
// _gpuTexture(Texture::createFromStorage(this))*/
|
|
||||||
}
|
|
||||||
|
|
||||||
TextureSource::~TextureSource() {
|
void TextureMap::setTextureSource(TextureSourcePointer& textureSource) {
|
||||||
}
|
_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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TextureMap::isDefined() const {
|
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;
|
QImage image = srcImage;
|
||||||
|
|
||||||
int imageArea = image.width() * image.height();
|
int imageArea = image.width() * image.height();
|
||||||
|
@ -175,7 +149,7 @@ double mapComponent(double sobelValue) {
|
||||||
return (sobelValue + 1.0) * factor;
|
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;
|
QImage image = srcImage;
|
||||||
|
|
||||||
// PR 5540 by AlessandroSigna
|
// PR 5540 by AlessandroSigna
|
||||||
|
@ -285,7 +259,7 @@ public:
|
||||||
_faceZNeg(fZN) {}
|
_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;
|
QImage image = srcImage;
|
||||||
|
|
||||||
int imageArea = image.width() * image.height();
|
int imageArea = image.width() * image.height();
|
||||||
|
|
|
@ -30,36 +30,11 @@ public:
|
||||||
Material::MapFlags _materialUsage{ MaterialKey::DIFFUSE_MAP };
|
Material::MapFlags _materialUsage{ MaterialKey::DIFFUSE_MAP };
|
||||||
|
|
||||||
int _environmentUsage = 0;
|
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* create2DTextureFromImage(const QImage& image, const std::string& srcImageName);
|
||||||
static gpu::Texture* createNormalTextureFromBumpImage(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);
|
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:
|
public:
|
||||||
TextureMap() {}
|
TextureMap() {}
|
||||||
|
|
||||||
void setTextureSource(TextureSourcePointer& texStorage);
|
void setTextureSource(gpu::TextureSourcePointer& textureSource);
|
||||||
|
|
||||||
bool isDefined() const;
|
bool isDefined() const;
|
||||||
gpu::TextureView getTextureView() const;
|
gpu::TextureView getTextureView() const;
|
||||||
|
@ -79,7 +54,7 @@ public:
|
||||||
const glm::vec2& getLightmapOffsetScale() const { return _lightmapOffsetScale; }
|
const glm::vec2& getLightmapOffsetScale() const { return _lightmapOffsetScale; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
TextureSourcePointer _textureSource;
|
gpu::TextureSourcePointer _textureSource;
|
||||||
|
|
||||||
Transform _texcoordTransform;
|
Transform _texcoordTransform;
|
||||||
glm::vec2 _lightmapOffsetScale{ 0.0f, 1.0f };
|
glm::vec2 _lightmapOffsetScale{ 0.0f, 1.0f };
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
set(TARGET_NAME procedural)
|
set(TARGET_NAME procedural)
|
||||||
|
|
||||||
|
AUTOSCRIBE_SHADER_LIB(gpu model)
|
||||||
|
|
||||||
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
||||||
setup_hifi_library()
|
setup_hifi_library()
|
||||||
|
|
||||||
|
@ -7,4 +9,4 @@ add_dependency_external_projects(glm)
|
||||||
find_package(GLM REQUIRED)
|
find_package(GLM REQUIRED)
|
||||||
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
|
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)
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
#include <QtCore/QJsonDocument>
|
#include <QtCore/QJsonDocument>
|
||||||
#include <QtCore/QJsonObject>
|
#include <QtCore/QJsonObject>
|
||||||
|
|
||||||
#include <gpu-networking/ShaderCache.h>
|
|
||||||
#include <gpu/Batch.h>
|
#include <gpu/Batch.h>
|
||||||
#include <SharedUtil.h>
|
#include <SharedUtil.h>
|
||||||
#include <NumericalConstants.h>
|
#include <NumericalConstants.h>
|
||||||
|
|
|
@ -18,8 +18,7 @@
|
||||||
#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 <gpu-networking/ShaderCache.h>
|
#include <model-networking/ShaderCache.h>
|
||||||
|
|
||||||
|
|
||||||
// FIXME better encapsulation
|
// FIXME better encapsulation
|
||||||
// FIXME better mechanism for extending to things rendered using shaders other than simple.slv
|
// FIXME better mechanism for extending to things rendered using shaders other than simple.slv
|
||||||
|
|
71
libraries/procedural/src/procedural/ProceduralSkybox.cpp
Normal file
71
libraries/procedural/src/procedural/ProceduralSkybox.cpp
Normal file
|
@ -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 <gpu/Batch.h>
|
||||||
|
#include <gpu/Context.h>
|
||||||
|
#include <ViewFrustum.h>
|
||||||
|
|
||||||
|
#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<gpu::Buffer>(sizeof(vertices), (const gpu::Byte*) vertices);
|
||||||
|
theFormat = std::make_shared<gpu::Stream::Format>();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
35
libraries/procedural/src/procedural/ProceduralSkybox.h
Normal file
35
libraries/procedural/src/procedural/ProceduralSkybox.h
Normal file
|
@ -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 <model/Skybox.h>
|
||||||
|
|
||||||
|
#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
|
50
libraries/procedural/src/procedural/ProceduralSkybox.slf
Normal file
50
libraries/procedural/src/procedural/ProceduralSkybox.slf
Normal file
|
@ -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
|
||||||
|
|
||||||
|
}
|
34
libraries/procedural/src/procedural/ProceduralSkybox.slv
Normal file
34
libraries/procedural/src/procedural/ProceduralSkybox.slv
Normal file
|
@ -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);
|
||||||
|
}
|
|
@ -40,4 +40,4 @@ add_dependency_external_projects(oglplus)
|
||||||
find_package(OGLPLUS REQUIRED)
|
find_package(OGLPLUS REQUIRED)
|
||||||
target_include_directories(${TARGET_NAME} PUBLIC ${OGLPLUS_INCLUDE_DIRS})
|
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)
|
||||||
|
|
|
@ -38,8 +38,6 @@ const int GeometryCache::UNKNOWN_ID = -1;
|
||||||
GeometryCache::GeometryCache() :
|
GeometryCache::GeometryCache() :
|
||||||
_nextID(0)
|
_nextID(0)
|
||||||
{
|
{
|
||||||
const qint64 GEOMETRY_DEFAULT_UNUSED_MAX_SIZE = DEFAULT_UNUSED_MAX_SIZE;
|
|
||||||
setUnusedResourceCacheSize(GEOMETRY_DEFAULT_UNUSED_MAX_SIZE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GeometryCache::~GeometryCache() {
|
GeometryCache::~GeometryCache() {
|
||||||
|
@ -51,13 +49,6 @@ GeometryCache::~GeometryCache() {
|
||||||
#endif //def WANT_DEBUG
|
#endif //def WANT_DEBUG
|
||||||
}
|
}
|
||||||
|
|
||||||
QSharedPointer<Resource> GeometryCache::createResource(const QUrl& url, const QSharedPointer<Resource>& 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<Resource>();
|
|
||||||
}
|
|
||||||
|
|
||||||
const int NUM_VERTICES_PER_TRIANGLE = 3;
|
const int NUM_VERTICES_PER_TRIANGLE = 3;
|
||||||
const int NUM_TRIANGLES_PER_QUAD = 2;
|
const int NUM_TRIANGLES_PER_QUAD = 2;
|
||||||
const int NUM_VERTICES_PER_TRIANGULATED_QUAD = NUM_VERTICES_PER_TRIANGLE * NUM_TRIANGLES_PER_QUAD;
|
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);
|
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<TextureCache>();
|
|
||||||
for (auto&& material : _materials) {
|
|
||||||
QSharedPointer<NetworkTexture> matchingTexture = QSharedPointer<NetworkTexture>();
|
|
||||||
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<TextureCache>();
|
|
||||||
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<gpu::Buffer>();
|
|
||||||
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<gpu::Buffer>();
|
|
||||||
// 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<gpu::BufferStream>();
|
|
||||||
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<gpu::Stream::Format>();
|
|
||||||
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<gpu::BufferStream>();
|
|
||||||
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<gpu::Stream::Format>();
|
|
||||||
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<TextureCache>();
|
|
||||||
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<QString, int> 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -12,14 +12,12 @@
|
||||||
#ifndef hifi_GeometryCache_h
|
#ifndef hifi_GeometryCache_h
|
||||||
#define hifi_GeometryCache_h
|
#define hifi_GeometryCache_h
|
||||||
|
|
||||||
|
#include "model-networking/ModelCache.h"
|
||||||
|
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QRunnable>
|
#include <QRunnable>
|
||||||
|
|
||||||
#include <DependencyManager.h>
|
#include <DependencyManager.h>
|
||||||
#include <ResourceCache.h>
|
|
||||||
|
|
||||||
#include "FBXReader.h"
|
|
||||||
#include "OBJReader.h"
|
|
||||||
|
|
||||||
#include <gpu/Batch.h>
|
#include <gpu/Batch.h>
|
||||||
#include <gpu/Stream.h>
|
#include <gpu/Stream.h>
|
||||||
|
@ -28,13 +26,6 @@
|
||||||
#include <model/Material.h>
|
#include <model/Material.h>
|
||||||
#include <model/Asset.h>
|
#include <model/Asset.h>
|
||||||
|
|
||||||
class NetworkGeometry;
|
|
||||||
class NetworkMesh;
|
|
||||||
class NetworkTexture;
|
|
||||||
class NetworkMaterial;
|
|
||||||
class NetworkShape;
|
|
||||||
|
|
||||||
|
|
||||||
typedef glm::vec3 Vec3Key;
|
typedef glm::vec3 Vec3Key;
|
||||||
|
|
||||||
typedef QPair<glm::vec2, glm::vec2> Vec2Pair;
|
typedef QPair<glm::vec2, glm::vec2> Vec2Pair;
|
||||||
|
@ -125,17 +116,13 @@ inline uint qHash(const Vec4PairVec4Pair& v, uint seed) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stores cached geometry.
|
/// Stores cached geometry.
|
||||||
class GeometryCache : public ResourceCache, public Dependency {
|
class GeometryCache : public Dependency {
|
||||||
Q_OBJECT
|
|
||||||
SINGLETON_DEPENDENCY
|
SINGLETON_DEPENDENCY
|
||||||
|
|
||||||
public:
|
public:
|
||||||
int allocateID() { return _nextID++; }
|
int allocateID() { return _nextID++; }
|
||||||
static const int UNKNOWN_ID;
|
static const int UNKNOWN_ID;
|
||||||
|
|
||||||
virtual QSharedPointer<Resource> createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
|
|
||||||
bool delayLoad, const void* extra);
|
|
||||||
|
|
||||||
gpu::BufferPointer getCubeVertices(float size);
|
gpu::BufferPointer getCubeVertices(float size);
|
||||||
void setupCubeVertices(gpu::Batch& batch, gpu::BufferPointer& verticesBuffer);
|
void setupCubeVertices(gpu::Batch& batch, gpu::BufferPointer& verticesBuffer);
|
||||||
|
|
||||||
|
@ -313,152 +300,6 @@ private:
|
||||||
QHash<Vec3Pair, gpu::BufferPointer> _sphereColors;
|
QHash<Vec3Pair, gpu::BufferPointer> _sphereColors;
|
||||||
QHash<int, gpu::BufferPointer> _registeredSphereColors;
|
QHash<int, gpu::BufferPointer> _registeredSphereColors;
|
||||||
QHash<int, Vec3Pair> _lastRegisteredSphereColors;
|
QHash<int, Vec3Pair> _lastRegisteredSphereColors;
|
||||||
|
|
||||||
QHash<QUrl, QWeakPointer<NetworkGeometry> > _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<std::unique_ptr<NetworkMesh>>& 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<FBXGeometry> _geometry; // This should go away evenutally once we can put everything we need in the model::AssetPointer
|
|
||||||
std::vector<std::unique_ptr<NetworkMesh>> _meshes;
|
|
||||||
std::vector<std::unique_ptr<NetworkMaterial>> _materials;
|
|
||||||
std::vector<std::unique_ptr<NetworkShape>> _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<NetworkTexture> diffuseTexture;
|
|
||||||
QString normalTextureName;
|
|
||||||
QSharedPointer<NetworkTexture> normalTexture;
|
|
||||||
QString specularTextureName;
|
|
||||||
QSharedPointer<NetworkTexture> specularTexture;
|
|
||||||
QString emissiveTextureName;
|
|
||||||
QSharedPointer<NetworkTexture> 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
|
#endif // hifi_GeometryCache_h
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
// Compatibility
|
// Compatibility
|
||||||
#include <gpu-networking/TextureCache.h>
|
#include <model-networking/TextureCache.h>
|
||||||
|
|
|
@ -7,4 +7,4 @@ add_dependency_external_projects(glm)
|
||||||
find_package(GLM REQUIRED)
|
find_package(GLM REQUIRED)
|
||||||
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
|
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)
|
||||||
|
|
|
@ -10,6 +10,6 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
|
||||||
#include_oglplus()
|
#include_oglplus()
|
||||||
|
|
||||||
# link in the shared libraries
|
# 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()
|
copy_dlls_beside_windows_executable()
|
Loading…
Reference in a new issue