* Removed validation logic from Resource class, Qt does this internally and is more standards compliant. This should result in more accurate caching and faster resource fetching when cache is stale and validation fails. * Added loaded and failed slots to Resource class, so it does not have to be polled. * NetworkGeometry now uses multiple Resource objects to download the fst/mapping file and the fbx/obj models. * NetworkGeometry is no longer a subclass of Resource * NetworkGeometry now has signals for success and failure, you no longer have to poll it to determine when loading is complete (except for textures *sigh*) Some functionality was removed * NetworkGeometry no longer has a fallback * NetworkGeometry no longer loads LODs or has lod logic. * The number of FBXGeometry copies is greatly reduced. * Model::setURL no supports fallback URL, delayLoad or retainCurrent option. This can result in a pop when switching avatars, and there's no longer a default if avatar loading fails.
Showing the changes to libraries/render-utils/src/GeometryCache.cpp (1 of 20 files). Show all
Unified · Split
| @@ -14,7 +14,6 @@ · 13 unmodified lines | |||
| 14 | #include <cmath> | 14 | #include <cmath> |
| 15 | 15 | ||
| 16 | #include <QNetworkReply> | 16 | #include <QNetworkReply> |
| 17 | #include <QRunnable> | ||
| 18 | #include <QThreadPool> | 17 | #include <QThreadPool> |
| 19 | 18 | ||
| 20 | #include <FSTReader.h> | 19 | #include <FSTReader.h> |
| @@ -50,6 +49,13 @@ GeometryCache::~GeometryCache() { · 29 unmodified lines | |||
| 50 | #endif //def WANT_DEBUG | 49 | #endif //def WANT_DEBUG |
| 51 | } | 50 | } |
| 52 | 51 | ||
| 52 | QSharedPointer<Resource> GeometryCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, | ||
| 53 | bool delayLoad, const void* extra) { | ||
| 54 | // NetworkGeometry is no longer a subclass of Resource, but requires this method because, it is pure virtual. | ||
| 55 | assert(false); | ||
| 56 | return QSharedPointer<Resource>(); | ||
| 57 | } | ||
| 58 | |||
| 53 | const int NUM_VERTICES_PER_TRIANGLE = 3; | 59 | const int NUM_VERTICES_PER_TRIANGLE = 3; |
| 54 | const int NUM_TRIANGLES_PER_QUAD = 2; | 60 | const int NUM_TRIANGLES_PER_QUAD = 2; |
| 55 | const int NUM_VERTICES_PER_TRIANGULATED_QUAD = NUM_VERTICES_PER_TRIANGLE * NUM_TRIANGLES_PER_QUAD; | 61 | const int NUM_VERTICES_PER_TRIANGULATED_QUAD = NUM_VERTICES_PER_TRIANGLE * NUM_TRIANGLES_PER_QUAD; |
| @@ -1643,19 +1649,6 @@ void GeometryCache::renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm · 1587 unmodified lines | |||
| 1643 | batch.draw(gpu::LINES, 2, 0); | 1649 | batch.draw(gpu::LINES, 2, 0); |
| 1644 | } | 1650 | } |
| 1645 | 1651 | ||
| 1646 | |||
| 1647 | QSharedPointer<NetworkGeometry> GeometryCache::getGeometry(const QUrl& url, const QUrl& fallback, bool delayLoad) { | ||
| 1648 | return getResource(url, fallback, delayLoad, NULL).staticCast<NetworkGeometry>(); | ||
| 1649 | } | ||
| 1650 | |||
| 1651 | QSharedPointer<Resource> GeometryCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, | ||
| 1652 | bool delayLoad, const void* extra) { | ||
| 1653 | QSharedPointer<NetworkGeometry> geometry(new NetworkGeometry(url, fallback.staticCast<NetworkGeometry>(), delayLoad), | ||
| 1654 | &Resource::allReferencesCleared); | ||
| 1655 | geometry->setLODParent(geometry); | ||
| 1656 | return geometry.staticCast<Resource>(); | ||
| 1657 | } | ||
| 1658 | |||
| 1659 | void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend) { | 1652 | void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend) { |
| 1660 | if (!_standardDrawPipeline) { | 1653 | if (!_standardDrawPipeline) { |
| 1661 | auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(standardTransformPNTC_vert))); | 1654 | auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(standardTransformPNTC_vert))); |
| @@ -1685,232 +1678,136 @@ void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend) { · 23 unmodified lines | |||
| 1685 | } | 1678 | } |
| 1686 | } | 1679 | } |
| 1687 | 1680 | ||
| 1688 | const float NetworkGeometry::NO_HYSTERESIS = -1.0f; | 1681 | GeometryReader::GeometryReader(const QUrl& url, QNetworkReply* reply, const QVariantHash& mapping) : |
| 1689 | 1682 | _url(url), | |
| 1690 | NetworkGeometry::NetworkGeometry(const QUrl& url, const QSharedPointer<NetworkGeometry>& fallback, bool delayLoad, | 1683 | _reply(reply), |
| 1691 | const QVariantHash& mapping, const QUrl& textureBase) : | 1684 | _mapping(mapping) { |
| 1692 | Resource(url, delayLoad), | ||
| 1693 | _mapping(mapping), | ||
| 1694 | _textureBase(textureBase.isValid() ? textureBase : url), | ||
| 1695 | _fallback(fallback) | ||
| 1696 | { | ||
| 1697 | |||
| 1698 | if (url.isEmpty()) { | ||
| 1699 | // make the minimal amount of dummy geometry to satisfy Model | ||
| 1700 | FBXJoint joint = { false, QVector<int>(), -1, 0.0f, 0.0f, glm::vec3(), glm::mat4(), glm::quat(), glm::quat(), | ||
| 1701 | glm::quat(), glm::mat4(), glm::mat4(), glm::vec3(), glm::vec3(), glm::quat(), glm::quat(), | ||
| 1702 | glm::mat4(), QString(""), false}; | ||
| 1703 | _geometry.joints.append(joint); | ||
| 1704 | _geometry.leftEyeJointIndex = -1; | ||
| 1705 | _geometry.rightEyeJointIndex = -1; | ||
| 1706 | _geometry.neckJointIndex = -1; | ||
| 1707 | _geometry.rootJointIndex = -1; | ||
| 1708 | _geometry.leanJointIndex = -1; | ||
| 1709 | _geometry.headJointIndex = -1; | ||
| 1710 | _geometry.leftHandJointIndex = -1; | ||
| 1711 | _geometry.rightHandJointIndex = -1; | ||
| 1712 | } | ||
| 1713 | |||
| 1714 | connect(this, &Resource::loaded, this, &NetworkGeometry::replaceTexturesWithPendingChanges); | ||
| 1715 | } | 1685 | } |
| 1716 | 1686 | ||
| 1717 | bool NetworkGeometry::isLoadedWithTextures() const { | 1687 | void GeometryReader::run() { |
| 1718 | if (!isLoaded()) { | 1688 | try { |
| 1719 | return false; | 1689 | if (!_reply) { |
| 1720 | } | 1690 | throw QString("Reply is NULL ?!"); |
| 1721 | if (!_isLoadedWithTextures) { | ||
| 1722 | foreach (const NetworkMesh& mesh, _meshes) { | ||
| 1723 | foreach (const NetworkMeshPart& part, mesh.parts) { | ||
| 1724 | if ((part.diffuseTexture && !part.diffuseTexture->isLoaded()) || | ||
| 1725 | (part.normalTexture && !part.normalTexture->isLoaded()) || | ||
| 1726 | (part.specularTexture && !part.specularTexture->isLoaded()) || | ||
| 1727 | (part.emissiveTexture && !part.emissiveTexture->isLoaded())) { | ||
| 1728 | return false; | ||
| 1729 | } | ||
| 1730 | } | ||
| 1731 | } | 1691 | } |
| 1732 | _isLoadedWithTextures = true; | 1692 | QString urlname = _url.path().toLower(); |
| 1733 | } | 1693 | bool urlValid = true; |
| 1734 | return true; | 1694 | urlValid &= !urlname.isEmpty(); |
| 1735 | } | 1695 | urlValid &= !_url.path().isEmpty(); |
| 1696 | urlValid &= _url.path().toLower().endsWith(".fbx") || _url.path().toLower().endsWith(".obj"); | ||
| 1736 | 1697 | ||
| 1737 | QSharedPointer<NetworkGeometry> NetworkGeometry::getLODOrFallback(float distance, float& hysteresis, bool delayLoad) const { | 1698 | if (urlValid) { |
| 1738 | if (_lodParent.data() != this) { | 1699 | // Let's read the binaries from the network |
| 1739 | return _lodParent.data()->getLODOrFallback(distance, hysteresis, delayLoad); | 1700 | FBXGeometry* fbxgeo = nullptr; |
| 1740 | } | 1701 | if (_url.path().toLower().endsWith(".fbx")) { |
| 1741 | if (_failedToLoad && _fallback) { | 1702 | const bool grabLightmaps = true; |
| 1742 | return _fallback; | 1703 | const float lightmapLevel = 1.0f; |
| 1743 | } | 1704 | fbxgeo = readFBX(_reply, _mapping, _url.path(), grabLightmaps, lightmapLevel); |
| 1744 | QSharedPointer<NetworkGeometry> lod = _lodParent; | 1705 | } else if (_url.path().toLower().endsWith(".obj")) { |
| 1745 | float lodDistance = 0.0f; | 1706 | fbxgeo = OBJReader().readOBJ(_reply, _mapping, &_url); |
| 1746 | QMap<float, QSharedPointer<NetworkGeometry> >::const_iterator it = _lods.upperBound(distance); | 1707 | } else { |
| 1747 | if (it != _lods.constBegin()) { | 1708 | QString errorStr("usupported format"); |
| 1748 | it = it - 1; | 1709 | emit onError(NetworkGeometry::ModelParseError, errorStr); |
| 1749 | lod = it.value(); | ||
| 1750 | lodDistance = it.key(); | ||
| 1751 | } | ||
| 1752 | if (hysteresis != NO_HYSTERESIS && hysteresis != lodDistance) { | ||
| 1753 | // if we previously selected a different distance, make sure we've moved far enough to justify switching | ||
| 1754 | const float HYSTERESIS_PROPORTION = 0.1f; | ||
| 1755 | if (glm::abs(distance - qMax(hysteresis, lodDistance)) / fabsf(hysteresis - lodDistance) < HYSTERESIS_PROPORTION) { | ||
| 1756 | lod = _lodParent; | ||
| 1757 | lodDistance = 0.0f; | ||
| 1758 | it = _lods.upperBound(hysteresis); | ||
| 1759 | if (it != _lods.constBegin()) { | ||
| 1760 | it = it - 1; | ||
| 1761 | lod = it.value(); | ||
| 1762 | lodDistance = it.key(); | ||
| 1763 | } | 1710 | } |
| 1711 | emit onSuccess(fbxgeo); | ||
| 1712 | } else { | ||
| 1713 | throw QString("url is invalid"); | ||
| 1764 | } | 1714 | } |
| 1715 | |||
| 1716 | } catch (const QString& error) { | ||
| 1717 | qCDebug(renderutils) << "Error reading " << _url << ": " << error; | ||
| 1718 | emit onError(NetworkGeometry::ModelParseError, error); | ||
| 1765 | } | 1719 | } |
| 1766 | if (lod && lod->isLoaded()) { | 1720 | _reply->deleteLater(); |
| 1767 | hysteresis = lodDistance; | ||
| 1768 | return lod; | ||
| 1769 | } | ||
| 1770 | // if the ideal LOD isn't loaded, we need to make sure it's started to load, and possibly return the closest loaded one | ||
| 1771 | if (!delayLoad) { | ||
| 1772 | lod->ensureLoading(); | ||
| 1773 | } | ||
| 1774 | float closestDistance = FLT_MAX; | ||
| 1775 | if (isLoaded()) { | ||
| 1776 | lod = _lodParent; | ||
| 1777 | closestDistance = distance; | ||
| 1778 | } | ||
| 1779 | for (it = _lods.constBegin(); it != _lods.constEnd(); it++) { | ||
| 1780 | float distanceToLOD = glm::abs(distance - it.key()); | ||
| 1781 | if (it.value()->isLoaded() && distanceToLOD < closestDistance) { | ||
| 1782 | lod = it.value(); | ||
| 1783 | closestDistance = distanceToLOD; | ||
| 1784 | } | ||
| 1785 | } | ||
| 1786 | hysteresis = NO_HYSTERESIS; | ||
| 1787 | return lod; | ||
| 1788 | } | 1721 | } |
| 1789 | 1722 | ||
| 1790 | uint qHash(const QWeakPointer<Animation>& animation, uint seed = 0) { | 1723 | NetworkGeometry::NetworkGeometry(const QUrl& url, bool delayLoad, const QVariantHash& mapping, const QUrl& textureBaseUrl) : |
| 1791 | return qHash(animation.data(), seed); | 1724 | _url(url), |
| 1725 | _mapping(mapping), | ||
| 1726 | _textureBaseUrl(textureBaseUrl) { | ||
| 1727 | |||
| 1728 | if (delayLoad) { | ||
| 1729 | _state = DelayState; | ||
| 1730 | } else { | ||
| 1731 | attemptRequestInternal(); | ||
| 1732 | } | ||
| 1792 | } | 1733 | } |
| 1793 | 1734 | ||
| 1794 | QVector<int> NetworkGeometry::getJointMappings(const AnimationPointer& animation) { | 1735 | NetworkGeometry::~NetworkGeometry() { |
| 1795 | QVector<int> mappings = _jointMappings.value(animation); | 1736 | if (_resource) { |
| 1796 | if (mappings.isEmpty() && isLoaded() && animation && animation->isLoaded()) { | 1737 | _resource->deleteLater(); |
| 1797 | const FBXGeometry& animationGeometry = animation->getGeometry(); | ||
| 1798 | for (int i = 0; i < animationGeometry.joints.size(); i++) { | ||
| 1799 | mappings.append(_geometry.jointIndices.value(animationGeometry.joints.at(i).name) - 1); | ||
| 1800 | } | ||
| 1801 | _jointMappings.insert(animation, mappings); | ||
| 1802 | } | 1738 | } |
| 1803 | return mappings; | ||
| 1804 | } | 1739 | } |
| 1805 | 1740 | ||
| 1806 | void NetworkGeometry::setLoadPriority(const QPointer<QObject>& owner, float priority) { | 1741 | void NetworkGeometry::attemptRequest() { |
| 1807 | Resource::setLoadPriority(owner, priority); | 1742 | if (_state == DelayState) { |
| 1808 | 1743 | attemptRequestInternal(); | |
| 1809 | for (int i = 0; i < _meshes.size(); i++) { | ||
| 1810 | NetworkMesh& mesh = _meshes[i]; | ||
| 1811 | for (int j = 0; j < mesh.parts.size(); j++) { | ||
| 1812 | NetworkMeshPart& part = mesh.parts[j]; | ||
| 1813 | if (part.diffuseTexture) { | ||
| 1814 | part.diffuseTexture->setLoadPriority(owner, priority); | ||
| 1815 | } | ||
| 1816 | if (part.normalTexture) { | ||
| 1817 | part.normalTexture->setLoadPriority(owner, priority); | ||
| 1818 | } | ||
| 1819 | if (part.specularTexture) { | ||
| 1820 | part.specularTexture->setLoadPriority(owner, priority); | ||
| 1821 | } | ||
| 1822 | if (part.emissiveTexture) { | ||
| 1823 | part.emissiveTexture->setLoadPriority(owner, priority); | ||
| 1824 | } | ||
| 1825 | } | ||
| 1826 | } | 1744 | } |
| 1827 | } | 1745 | } |
| 1828 | 1746 | ||
| 1829 | void NetworkGeometry::setLoadPriorities(const QHash<QPointer<QObject>, float>& priorities) { | 1747 | void NetworkGeometry::attemptRequestInternal() { |
| 1830 | Resource::setLoadPriorities(priorities); | 1748 | if (_url.path().toLower().endsWith(".fst")) { |
| 1831 | 1749 | requestMapping(_url); | |
| 1832 | for (int i = 0; i < _meshes.size(); i++) { | 1750 | } else { |
| 1833 | NetworkMesh& mesh = _meshes[i]; | 1751 | requestModel(_url); |
| 1834 | for (int j = 0; j < mesh.parts.size(); j++) { | ||
| 1835 | NetworkMeshPart& part = mesh.parts[j]; | ||
| 1836 | if (part.diffuseTexture) { | ||
| 1837 | part.diffuseTexture->setLoadPriorities(priorities); | ||
| 1838 | } | ||
| 1839 | if (part.normalTexture) { | ||
| 1840 | part.normalTexture->setLoadPriorities(priorities); | ||
| 1841 | } | ||
| 1842 | if (part.specularTexture) { | ||
| 1843 | part.specularTexture->setLoadPriorities(priorities); | ||
| 1844 | } | ||
| 1845 | if (part.emissiveTexture) { | ||
| 1846 | part.emissiveTexture->setLoadPriorities(priorities); | ||
| 1847 | } | ||
| 1848 | } | ||
| 1849 | } | 1752 | } |
| 1850 | } | 1753 | } |
| 1851 | 1754 | ||
| 1852 | void NetworkGeometry::clearLoadPriority(const QPointer<QObject>& owner) { | 1755 | bool NetworkGeometry::isLoaded() const { |
| 1853 | Resource::clearLoadPriority(owner); | 1756 | return _state == SuccessState; |
| 1854 | 1757 | } | |
| 1855 | for (int i = 0; i < _meshes.size(); i++) { | 1758 | |
| 1856 | NetworkMesh& mesh = _meshes[i]; | 1759 | bool NetworkGeometry::isLoadedWithTextures() const { |
| 1857 | for (int j = 0; j < mesh.parts.size(); j++) { | 1760 | if (!isLoaded()) { |
| 1858 | NetworkMeshPart& part = mesh.parts[j]; | 1761 | return false; |
| 1859 | if (part.diffuseTexture) { | 1762 | } |
| 1860 | part.diffuseTexture->clearLoadPriority(owner); | 1763 | if (!_isLoadedWithTextures) { |
| 1861 | } | 1764 | for (auto&& mesh : _meshes) { |
| 1862 | if (part.normalTexture) { | 1765 | for (auto && part : mesh->_parts) { |
| 1863 | part.normalTexture->clearLoadPriority(owner); | 1766 | if ((part->diffuseTexture && !part->diffuseTexture->isLoaded()) || |
| 1864 | } | 1767 | (part->normalTexture && !part->normalTexture->isLoaded()) || |
| 1865 | if (part.specularTexture) { | 1768 | (part->specularTexture && !part->specularTexture->isLoaded()) || |
| 1866 | part.specularTexture->clearLoadPriority(owner); | 1769 | (part->emissiveTexture && !part->emissiveTexture->isLoaded())) { |
| 1867 | } | 1770 | return false; |
| 1868 | if (part.emissiveTexture) { | 1771 | } |
| 1869 | part.emissiveTexture->clearLoadPriority(owner); | ||
| 1870 | } | 1772 | } |
| 1871 | } | 1773 | } |
| 1774 | _isLoadedWithTextures = true; | ||
| 1872 | } | 1775 | } |
| 1776 | return true; | ||
| 1873 | } | 1777 | } |
| 1874 | 1778 | ||
| 1875 | void NetworkGeometry::setTextureWithNameToURL(const QString& name, const QUrl& url) { | 1779 | void NetworkGeometry::setTextureWithNameToURL(const QString& name, const QUrl& url) { |
| 1876 | if (_meshes.size() > 0) { | 1780 | if (_meshes.size() > 0) { |
| 1877 | auto textureCache = DependencyManager::get<TextureCache>(); | 1781 | auto textureCache = DependencyManager::get<TextureCache>(); |
| 1878 | for (int i = 0; i < _meshes.size(); i++) { | 1782 | for (size_t i = 0; i < _meshes.size(); i++) { |
| 1879 | NetworkMesh& mesh = _meshes[i]; | 1783 | NetworkMesh& mesh = *(_meshes[i].get()); |
| 1880 | for (int j = 0; j < mesh.parts.size(); j++) { | 1784 | for (size_t j = 0; j < mesh._parts.size(); j++) { |
| 1881 | NetworkMeshPart& part = mesh.parts[j]; | 1785 | NetworkMeshPart& part = *(mesh._parts[j].get()); |
| 1882 | |||
| 1883 | QSharedPointer<NetworkTexture> matchingTexture = QSharedPointer<NetworkTexture>(); | 1786 | QSharedPointer<NetworkTexture> matchingTexture = QSharedPointer<NetworkTexture>(); |
| 1884 | if (part.diffuseTextureName == name) { | 1787 | if (part.diffuseTextureName == name) { |
| 1885 | part.diffuseTexture = textureCache->getTexture(url, DEFAULT_TEXTURE, _geometry.meshes[i].isEye); | 1788 | part.diffuseTexture = textureCache->getTexture(url, DEFAULT_TEXTURE, _geometry->meshes[i].isEye); |
| 1886 | part.diffuseTexture->setLoadPriorities(_loadPriorities); | ||
| 1887 | } else if (part.normalTextureName == name) { | 1789 | } else if (part.normalTextureName == name) { |
| 1888 | part.normalTexture = textureCache->getTexture(url); | 1790 | part.normalTexture = textureCache->getTexture(url); |
| 1889 | part.normalTexture->setLoadPriorities(_loadPriorities); | ||
| 1890 | } else if (part.specularTextureName == name) { | 1791 | } else if (part.specularTextureName == name) { |
| 1891 | part.specularTexture = textureCache->getTexture(url); | 1792 | part.specularTexture = textureCache->getTexture(url); |
| 1892 | part.specularTexture->setLoadPriorities(_loadPriorities); | ||
| 1893 | } else if (part.emissiveTextureName == name) { | 1793 | } else if (part.emissiveTextureName == name) { |
| 1894 | part.emissiveTexture = textureCache->getTexture(url); | 1794 | part.emissiveTexture = textureCache->getTexture(url); |
| 1895 | part.emissiveTexture->setLoadPriorities(_loadPriorities); | ||
| 1896 | } | 1795 | } |
| 1897 | } | 1796 | } |
| 1898 | } | 1797 | } |
| 1899 | } else { | 1798 | } else { |
| 1900 | qCDebug(renderutils) << "Adding a name url pair to pending" << name << url; | 1799 | qCWarning(renderutils) << "Ignoring setTextureWirthNameToURL() geometry not ready." << name << url; |
| 1901 | // we don't have meshes downloaded yet, so hold this texture as pending | ||
| 1902 | _pendingTextureChanges.insert(name, url); | ||
| 1903 | } | 1800 | } |
| 1904 | _isLoadedWithTextures = false; | 1801 | _isLoadedWithTextures = false; |
| 1905 | } | 1802 | } |
| 1906 | 1803 | ||
| 1907 | QStringList NetworkGeometry::getTextureNames() const { | 1804 | QStringList NetworkGeometry::getTextureNames() const { |
| 1908 | QStringList result; | 1805 | QStringList result; |
| 1909 | for (int i = 0; i < _meshes.size(); i++) { | 1806 | for (size_t i = 0; i < _meshes.size(); i++) { |
| 1910 | const NetworkMesh& mesh = _meshes[i]; | 1807 | const NetworkMesh& mesh = *(_meshes[i].get()); |
| 1911 | for (int j = 0; j < mesh.parts.size(); j++) { | 1808 | for (size_t j = 0; j < mesh._parts.size(); j++) { |
| 1912 | const NetworkMeshPart& part = mesh.parts[j]; | 1809 | const NetworkMeshPart& part = *(mesh._parts[j].get()); |
| 1913 | 1810 | ||
| 1914 | if (!part.diffuseTextureName.isEmpty() && part.diffuseTexture) { | 1811 | if (!part.diffuseTextureName.isEmpty() && part.diffuseTexture) { |
| 1915 | QString textureURL = part.diffuseTexture->getURL().toString(); | 1812 | QString textureURL = part.diffuseTexture->getURL().toString(); |
| 1916 | result << part.diffuseTextureName + ":" + textureURL; | 1813 | result << part.diffuseTextureName + ":" + textureURL; |
| @@ -1935,320 +1832,259 @@ QStringList NetworkGeometry::getTextureNames() const { · 18 unmodified lines | |||
| 1935 | return result; | 1832 | return result; |
| 1936 | } | 1833 | } |
| 1937 | 1834 | ||
| 1938 | void NetworkGeometry::replaceTexturesWithPendingChanges() { | 1835 | void NetworkGeometry::requestMapping(const QUrl& url) { |
| 1939 | QHash<QString, QUrl>::Iterator it = _pendingTextureChanges.begin(); | 1836 | _state = RequestMappingState; |
| 1940 | 1837 | if (_resource) { | |
| 1941 | while (it != _pendingTextureChanges.end()) { | 1838 | _resource->deleteLater(); |
| 1942 | setTextureWithNameToURL(it.key(), it.value()); | ||
| 1943 | it = _pendingTextureChanges.erase(it); | ||
| 1944 | } | 1839 | } |
| 1840 | _resource = new Resource(url, false); | ||
| 1841 | connect(_resource, SIGNAL(loaded(QNetworkReply&)), SLOT(mappingRequestDone(QNetworkReply&))); | ||
| 1842 | connect(_resource, SIGNAL(failed(QNetworkReply::NetworkError)), SLOT(mappingRequestError(QNetworkReply::NetworkError))); | ||
| 1945 | } | 1843 | } |
| 1946 | 1844 | ||
| 1947 | /// Reads geometry in a worker thread. | 1845 | void NetworkGeometry::requestModel(const QUrl& url) { |
| 1948 | class GeometryReader : public QRunnable { | 1846 | _state = RequestModelState; |
| 1949 | public: | 1847 | if (_resource) { |
| 1950 | 1848 | _resource->deleteLater(); | |
| 1951 | GeometryReader(const QWeakPointer<Resource>& geometry, const QUrl& url, | 1849 | } |
| 1952 | QNetworkReply* reply, const QVariantHash& mapping); | 1850 | _resource = new Resource(url, false); |
| 1953 | 1851 | connect(_resource, SIGNAL(loaded(QNetworkReply&)), SLOT(modelRequestDone(QNetworkReply&))); | |
| 1954 | virtual void run(); | 1852 | connect(_resource, SIGNAL(failed(QNetworkReply::NetworkError)), SLOT(modelRequestError(QNetworkReply::NetworkError))); |
| 1955 | |||
| 1956 | private: | ||
| 1957 | |||
| 1958 | QWeakPointer<Resource> _geometry; | ||
| 1959 | QUrl _url; | ||
| 1960 | QNetworkReply* _reply; | ||
| 1961 | QVariantHash _mapping; | ||
| 1962 | }; | ||
| 1963 | |||
| 1964 | GeometryReader::GeometryReader(const QWeakPointer<Resource>& geometry, const QUrl& url, | ||
| 1965 | QNetworkReply* reply, const QVariantHash& mapping) : | ||
| 1966 | _geometry(geometry), | ||
| 1967 | _url(url), | ||
| 1968 | _reply(reply), | ||
| 1969 | _mapping(mapping) { | ||
| 1970 | } | 1853 | } |
| 1971 | 1854 | ||
| 1972 | void GeometryReader::run() { | 1855 | void NetworkGeometry::mappingRequestDone(QNetworkReply& reply) { |
| 1973 | QSharedPointer<Resource> geometry = _geometry.toStrongRef(); | 1856 | assert(_state == RequestMappingState); |
| 1974 | if (geometry.isNull()) { | ||
| 1975 | _reply->deleteLater(); | ||
| 1976 | return; | ||
| 1977 | } | ||
| 1978 | try { | ||
| 1979 | if (!_reply) { | ||
| 1980 | throw QString("Reply is NULL ?!"); | ||
| 1981 | } | ||
| 1982 | QString urlname = _url.path().toLower(); | ||
| 1983 | bool urlValid = true; | ||
| 1984 | urlValid &= !urlname.isEmpty(); | ||
| 1985 | urlValid &= !_url.path().isEmpty(); | ||
| 1986 | urlValid &= _url.path().toLower().endsWith(".fbx") | ||
| 1987 | || _url.path().toLower().endsWith(".obj") | ||
| 1988 | || _url.path().toLower().endsWith(".svo"); | ||
| 1989 | 1857 | ||
| 1990 | if (urlValid) { | 1858 | // parse the mapping file |
| 1991 | // Let's read the binaries from the network | 1859 | _mapping = FSTReader::readMapping(reply.readAll()); |
| 1992 | FBXGeometry fbxgeo; | 1860 | |
| 1993 | if (_url.path().toLower().endsWith(".fbx")) { | 1861 | QUrl replyUrl = reply.url(); |
| 1994 | bool grabLightmaps = true; | 1862 | QString modelUrlStr = _mapping.value("filename").toString(); |
| 1995 | float lightmapLevel = 1.0f; | 1863 | if (modelUrlStr.isNull()) { |
| 1996 | // HACK: For monday 12/01/2014 we need to kill lighmaps loading in starchamber... | 1864 | qCDebug(renderutils) << "Mapping file " << _url << "has no \"filename\" entry"; |
| 1997 | if (_url.path().toLower().endsWith("loungev4_11-18.fbx")) { | 1865 | emit onFailure(*this, MissingFilenameInMapping); |
| 1998 | grabLightmaps = false; | 1866 | } else { |
| 1999 | } else if (_url.path().toLower().endsWith("apt8_reboot.fbx")) { | 1867 | // read _textureBase from mapping file, if present |
| 2000 | lightmapLevel = 4.0f; | 1868 | QString texdir = _mapping.value("texdir").toString(); |
| 2001 | } else if (_url.path().toLower().endsWith("palaceoforinthilian4.fbx")) { | 1869 | if (!texdir.isNull()) { |
| 2002 | lightmapLevel = 3.5f; | 1870 | if (!texdir.endsWith('/')) { |
| 2003 | } | 1871 | texdir += '/'; |
| 2004 | fbxgeo = readFBX(_reply, _mapping, _url.path(), grabLightmaps, lightmapLevel); | ||
| 2005 | } else if (_url.path().toLower().endsWith(".obj")) { | ||
| 2006 | fbxgeo = OBJReader().readOBJ(_reply, _mapping, &_url); | ||
| 2007 | } | 1872 | } |
| 2008 | QMetaObject::invokeMethod(geometry.data(), "setGeometry", Q_ARG(const FBXGeometry&, fbxgeo)); | 1873 | _textureBaseUrl = replyUrl.resolved(texdir); |
| 2009 | } else { | ||
| 2010 | throw QString("url is invalid"); | ||
| 2011 | } | 1874 | } |
| 2012 | 1875 | ||
| 2013 | } catch (const QString& error) { | 1876 | QUrl modelUrl = replyUrl.resolved(modelUrlStr); |
| 2014 | qCDebug(renderutils) << "Error reading " << _url << ": " << error; | 1877 | requestModel(modelUrl); |
| 2015 | QMetaObject::invokeMethod(geometry.data(), "finishedLoading", Q_ARG(bool, false)); | ||
| 2016 | } | 1878 | } |
| 2017 | _reply->deleteLater(); | ||
| 2018 | } | 1879 | } |
| 2019 | 1880 | ||
| 2020 | void NetworkGeometry::init() { | 1881 | void NetworkGeometry::mappingRequestError(QNetworkReply::NetworkError error) { |
| 2021 | _mapping = QVariantHash(); | 1882 | assert(_state == RequestMappingState); |
| 2022 | _geometry = FBXGeometry(); | 1883 | _state = ErrorState; |
| 2023 | _meshes.clear(); | 1884 | emit onFailure(*this, MappingRequestError); |
| 2024 | _lods.clear(); | ||
| 2025 | _pendingTextureChanges.clear(); | ||
| 2026 | _request.setUrl(_url); | ||
| 2027 | Resource::init(); | ||
| 2028 | } | 1885 | } |
| 2029 | 1886 | ||
| 2030 | void NetworkGeometry::downloadFinished(QNetworkReply* reply) { | 1887 | void NetworkGeometry::modelRequestDone(QNetworkReply& reply) { |
| 2031 | QUrl url = reply->url(); | 1888 | assert(_state == RequestModelState); |
| 2032 | if (url.path().toLower().endsWith(".fst")) { | ||
| 2033 | // it's a mapping file; parse it and get the mesh filename | ||
| 2034 | _mapping = FSTReader::readMapping(reply->readAll()); | ||
| 2035 | reply->deleteLater(); | ||
| 2036 | QString filename = _mapping.value("filename").toString(); | ||
| 2037 | if (filename.isNull()) { | ||
| 2038 | qCDebug(renderutils) << "Mapping file " << url << " has no filename."; | ||
| 2039 | finishedLoading(false); | ||
| 2040 | |||
| 2041 | } else { | ||
| 2042 | QString texdir = _mapping.value("texdir").toString(); | ||
| 2043 | if (!texdir.isNull()) { | ||
| 2044 | if (!texdir.endsWith('/')) { | ||
| 2045 | texdir += '/'; | ||
| 2046 | } | ||
| 2047 | _textureBase = url.resolved(texdir); | ||
| 2048 | } | ||
| 2049 | QVariantHash lods = _mapping.value("lod").toHash(); | ||
| 2050 | for (QVariantHash::const_iterator it = lods.begin(); it != lods.end(); it++) { | ||
| 2051 | auto geometry = QSharedPointer<NetworkGeometry>::create(url.resolved(it.key()), | ||
| 2052 | QSharedPointer<NetworkGeometry>(), true, _mapping, _textureBase); | ||
| 2053 | geometry->setSelf(geometry.staticCast<Resource>()); | ||
| 2054 | geometry->setLODParent(_lodParent); | ||
| 2055 | _lods.insert(it.value().toFloat(), geometry); | ||
| 2056 | } | ||
| 2057 | _request.setUrl(url.resolved(filename)); | ||
| 2058 | |||
| 2059 | // make the request immediately only if we have no LODs to switch between | ||
| 2060 | _startedLoading = false; | ||
| 2061 | if (_lods.isEmpty()) { | ||
| 2062 | attemptRequest(); | ||
| 2063 | } | ||
| 2064 | } | ||
| 2065 | return; | ||
| 2066 | } | ||
| 2067 | |||
| 2068 | // send the reader off to the thread pool | ||
| 2069 | QThreadPool::globalInstance()->start(new GeometryReader(_self, url, reply, _mapping)); | ||
| 2070 | } | ||
| 2071 | 1889 | ||
| 2072 | void NetworkGeometry::reinsert() { | 1890 | _state = ParsingModelState; |
| 2073 | Resource::reinsert(); | 1891 | |
| 2074 | 1892 | // asynchronously parse the model file. | |
| 2075 | _lodParent = qWeakPointerCast<NetworkGeometry, Resource>(_self); | 1893 | GeometryReader* geometryReader = new GeometryReader(reply.url(), &reply, _mapping); |
| 2076 | foreach (const QSharedPointer<NetworkGeometry>& lod, _lods) { | 1894 | connect(geometryReader, SIGNAL(onSuccess(FBXGeometry*)), SLOT(modelParseSuccess(FBXGeometry*))); |
| 2077 | lod->setLODParent(_lodParent); | 1895 | connect(geometryReader, SIGNAL(onError(int, QString)), SLOT(modelParseError(int, QString))); |
| 2078 | } | 1896 | |
| 1897 | QThreadPool::globalInstance()->start(geometryReader); | ||
| 2079 | } | 1898 | } |
| 2080 | 1899 | ||
| 2081 | void NetworkGeometry::setGeometry(const FBXGeometry& geometry) { | 1900 | void NetworkGeometry::modelRequestError(QNetworkReply::NetworkError error) { |
| 2082 | _geometry = geometry; | 1901 | assert(_state == RequestModelState); |
| 1902 | _state = ErrorState; | ||
| 1903 | emit onFailure(*this, ModelRequestError); | ||
| 1904 | } | ||
| 2083 | 1905 | ||
| 1906 | static NetworkMesh* buildNetworkMesh(const FBXMesh& mesh, const QUrl& textureBaseUrl) { | ||
| 2084 | auto textureCache = DependencyManager::get<TextureCache>(); | 1907 | auto textureCache = DependencyManager::get<TextureCache>(); |
| 2085 | 1908 | NetworkMesh* networkMesh = new NetworkMesh(); | |
| 2086 | foreach (const FBXMesh& mesh, _geometry.meshes) { | 1909 | |
| 2087 | NetworkMesh networkMesh; | 1910 | int totalIndices = 0; |
| 2088 | 1911 | bool checkForTexcoordLightmap = false; | |
| 2089 | int totalIndices = 0; | 1912 | |
| 2090 | bool checkForTexcoordLightmap = false; | 1913 | // process network parts |
| 2091 | foreach (const FBXMeshPart& part, mesh.parts) { | 1914 | foreach (const FBXMeshPart& part, mesh.parts) { |
| 2092 | NetworkMeshPart networkPart; | 1915 | NetworkMeshPart* networkPart = new NetworkMeshPart(); |
| 2093 | if (!part.diffuseTexture.filename.isEmpty()) { | 1916 | |
| 2094 | networkPart.diffuseTexture = textureCache->getTexture( | 1917 | if (!part.diffuseTexture.filename.isEmpty()) { |
| 2095 | _textureBase.resolved(QUrl(part.diffuseTexture.filename)), DEFAULT_TEXTURE, | 1918 | networkPart->diffuseTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(part.diffuseTexture.filename)), DEFAULT_TEXTURE, |
| 2096 | mesh.isEye, part.diffuseTexture.content); | 1919 | mesh.isEye, part.diffuseTexture.content); |
| 2097 | networkPart.diffuseTextureName = part.diffuseTexture.name; | 1920 | networkPart->diffuseTextureName = part.diffuseTexture.name; |
| 2098 | networkPart.diffuseTexture->setLoadPriorities(_loadPriorities); | ||
| 2099 | } | ||
| 2100 | if (!part.normalTexture.filename.isEmpty()) { | ||
| 2101 | networkPart.normalTexture = textureCache->getTexture( | ||
| 2102 | _textureBase.resolved(QUrl(part.normalTexture.filename)), NORMAL_TEXTURE, | ||
| 2103 | false, part.normalTexture.content); | ||
| 2104 | networkPart.normalTextureName = part.normalTexture.name; | ||
| 2105 | networkPart.normalTexture->setLoadPriorities(_loadPriorities); | ||
| 2106 | } | ||
| 2107 | if (!part.specularTexture.filename.isEmpty()) { | ||
| 2108 | networkPart.specularTexture = textureCache->getTexture( | ||
| 2109 | _textureBase.resolved(QUrl(part.specularTexture.filename)), SPECULAR_TEXTURE, | ||
| 2110 | false, part.specularTexture.content); | ||
| 2111 | networkPart.specularTextureName = part.specularTexture.name; | ||
| 2112 | networkPart.specularTexture->setLoadPriorities(_loadPriorities); | ||
| 2113 | } | ||
| 2114 | if (!part.emissiveTexture.filename.isEmpty()) { | ||
| 2115 | networkPart.emissiveTexture = textureCache->getTexture( | ||
| 2116 | _textureBase.resolved(QUrl(part.emissiveTexture.filename)), EMISSIVE_TEXTURE, | ||
| 2117 | false, part.emissiveTexture.content); | ||
| 2118 | networkPart.emissiveTextureName = part.emissiveTexture.name; | ||
| 2119 | networkPart.emissiveTexture->setLoadPriorities(_loadPriorities); | ||
| 2120 | checkForTexcoordLightmap = true; | ||
| 2121 | } | ||
| 2122 | networkMesh.parts.append(networkPart); | ||
| 2123 | |||
| 2124 | totalIndices += (part.quadIndices.size() + part.triangleIndices.size()); | ||
| 2125 | } | 1921 | } |
| 1922 | if (!part.normalTexture.filename.isEmpty()) { | ||
| 1923 | networkPart->normalTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(part.normalTexture.filename)), NORMAL_TEXTURE, | ||
| 1924 | false, part.normalTexture.content); | ||
| 1925 | networkPart->normalTextureName = part.normalTexture.name; | ||
| 1926 | } | ||
| 1927 | if (!part.specularTexture.filename.isEmpty()) { | ||
| 1928 | networkPart->specularTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(part.specularTexture.filename)), SPECULAR_TEXTURE, | ||
| 1929 | false, part.specularTexture.content); | ||
| 1930 | networkPart->specularTextureName = part.specularTexture.name; | ||
| 1931 | } | ||
| 1932 | if (!part.emissiveTexture.filename.isEmpty()) { | ||
| 1933 | networkPart->emissiveTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(part.emissiveTexture.filename)), EMISSIVE_TEXTURE, | ||
| 1934 | false, part.emissiveTexture.content); | ||
| 1935 | networkPart->emissiveTextureName = part.emissiveTexture.name; | ||
| 1936 | checkForTexcoordLightmap = true; | ||
| 1937 | } | ||
| 1938 | networkMesh->_parts.emplace_back(networkPart); | ||
| 1939 | totalIndices += (part.quadIndices.size() + part.triangleIndices.size()); | ||
| 1940 | } | ||
| 1941 | |||
| 1942 | // initialize index buffer | ||
| 1943 | { | ||
| 1944 | networkMesh->_indexBuffer = std::make_shared<gpu::Buffer>(); | ||
| 1945 | networkMesh->_indexBuffer->resize(totalIndices * sizeof(int)); | ||
| 1946 | int offset = 0; | ||
| 1947 | foreach(const FBXMeshPart& part, mesh.parts) { | ||
| 1948 | networkMesh->_indexBuffer->setSubData(offset, part.quadIndices.size() * sizeof(int), | ||
| 1949 | (gpu::Byte*) part.quadIndices.constData()); | ||
| 1950 | offset += part.quadIndices.size() * sizeof(int); | ||
| 1951 | networkMesh->_indexBuffer->setSubData(offset, part.triangleIndices.size() * sizeof(int), | ||
| 1952 | (gpu::Byte*) part.triangleIndices.constData()); | ||
| 1953 | offset += part.triangleIndices.size() * sizeof(int); | ||
| 1954 | } | ||
| 1955 | } | ||
| 2126 | 1956 | ||
| 2127 | { | 1957 | // initialize vertex buffer |
| 2128 | networkMesh._indexBuffer = std::make_shared<gpu::Buffer>(); | 1958 | { |
| 2129 | networkMesh._indexBuffer->resize(totalIndices * sizeof(int)); | 1959 | networkMesh->_vertexBuffer = std::make_shared<gpu::Buffer>(); |
| 2130 | int offset = 0; | 1960 | // if we don't need to do any blending, the positions/normals can be static |
| 2131 | foreach(const FBXMeshPart& part, mesh.parts) { | 1961 | if (mesh.blendshapes.isEmpty()) { |
| 2132 | networkMesh._indexBuffer->setSubData(offset, part.quadIndices.size() * sizeof(int), | 1962 | int normalsOffset = mesh.vertices.size() * sizeof(glm::vec3); |
| 2133 | (gpu::Byte*) part.quadIndices.constData()); | 1963 | int tangentsOffset = normalsOffset + mesh.normals.size() * sizeof(glm::vec3); |
| 2134 | offset += part.quadIndices.size() * sizeof(int); | 1964 | int colorsOffset = tangentsOffset + mesh.tangents.size() * sizeof(glm::vec3); |
| 2135 | networkMesh._indexBuffer->setSubData(offset, part.triangleIndices.size() * sizeof(int), | 1965 | int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3); |
| 2136 | (gpu::Byte*) part.triangleIndices.constData()); | 1966 | int texCoords1Offset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2); |
| 2137 | offset += part.triangleIndices.size() * sizeof(int); | 1967 | int clusterIndicesOffset = texCoords1Offset + mesh.texCoords1.size() * sizeof(glm::vec2); |
| 1968 | int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4); | ||
| 1969 | |||
| 1970 | networkMesh->_vertexBuffer->resize(clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4)); | ||
| 1971 | |||
| 1972 | networkMesh->_vertexBuffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.vertices.constData()); | ||
| 1973 | networkMesh->_vertexBuffer->setSubData(normalsOffset, mesh.normals.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.normals.constData()); | ||
| 1974 | networkMesh->_vertexBuffer->setSubData(tangentsOffset, | ||
| 1975 | mesh.tangents.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.tangents.constData()); | ||
| 1976 | networkMesh->_vertexBuffer->setSubData(colorsOffset, mesh.colors.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.colors.constData()); | ||
| 1977 | networkMesh->_vertexBuffer->setSubData(texCoordsOffset, | ||
| 1978 | mesh.texCoords.size() * sizeof(glm::vec2), (gpu::Byte*) mesh.texCoords.constData()); | ||
| 1979 | networkMesh->_vertexBuffer->setSubData(texCoords1Offset, | ||
| 1980 | mesh.texCoords1.size() * sizeof(glm::vec2), (gpu::Byte*) mesh.texCoords1.constData()); | ||
| 1981 | networkMesh->_vertexBuffer->setSubData(clusterIndicesOffset, | ||
| 1982 | mesh.clusterIndices.size() * sizeof(glm::vec4), (gpu::Byte*) mesh.clusterIndices.constData()); | ||
| 1983 | networkMesh->_vertexBuffer->setSubData(clusterWeightsOffset, | ||
| 1984 | mesh.clusterWeights.size() * sizeof(glm::vec4), (gpu::Byte*) mesh.clusterWeights.constData()); | ||
| 1985 | |||
| 1986 | // otherwise, at least the cluster indices/weights can be static | ||
| 1987 | networkMesh->_vertexStream = std::make_shared<gpu::BufferStream>(); | ||
| 1988 | networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, 0, sizeof(glm::vec3)); | ||
| 1989 | if (mesh.normals.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, normalsOffset, sizeof(glm::vec3)); | ||
| 1990 | if (mesh.tangents.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, tangentsOffset, sizeof(glm::vec3)); | ||
| 1991 | if (mesh.colors.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, colorsOffset, sizeof(glm::vec3)); | ||
| 1992 | if (mesh.texCoords.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, texCoordsOffset, sizeof(glm::vec2)); | ||
| 1993 | if (mesh.texCoords1.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, texCoords1Offset, sizeof(glm::vec2)); | ||
| 1994 | if (mesh.clusterIndices.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, clusterIndicesOffset, sizeof(glm::vec4)); | ||
| 1995 | if (mesh.clusterWeights.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, clusterWeightsOffset, sizeof(glm::vec4)); | ||
| 1996 | |||
| 1997 | int channelNum = 0; | ||
| 1998 | networkMesh->_vertexFormat = std::make_shared<gpu::Stream::Format>(); | ||
| 1999 | networkMesh->_vertexFormat->setAttribute(gpu::Stream::POSITION, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); | ||
| 2000 | if (mesh.normals.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::NORMAL, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); | ||
| 2001 | if (mesh.tangents.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::TANGENT, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); | ||
| 2002 | if (mesh.colors.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::COLOR, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RGB)); | ||
| 2003 | if (mesh.texCoords.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::TEXCOORD, channelNum++, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); | ||
| 2004 | if (mesh.texCoords1.size()) { | ||
| 2005 | networkMesh->_vertexFormat->setAttribute(gpu::Stream::TEXCOORD1, channelNum++, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); | ||
| 2006 | } else if (checkForTexcoordLightmap && mesh.texCoords.size()) { | ||
| 2007 | // need lightmap texcoord UV but doesn't have uv#1 so just reuse the same channel | ||
| 2008 | networkMesh->_vertexFormat->setAttribute(gpu::Stream::TEXCOORD1, channelNum - 1, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); | ||
| 2138 | } | 2009 | } |
| 2010 | if (mesh.clusterIndices.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_INDEX, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW)); | ||
| 2011 | if (mesh.clusterWeights.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW)); | ||
| 2139 | } | 2012 | } |
| 2013 | else { | ||
| 2014 | int colorsOffset = mesh.tangents.size() * sizeof(glm::vec3); | ||
| 2015 | int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3); | ||
| 2016 | int clusterIndicesOffset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2); | ||
| 2017 | int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4); | ||
| 2018 | |||
| 2019 | networkMesh->_vertexBuffer->resize(clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4)); | ||
| 2020 | networkMesh->_vertexBuffer->setSubData(0, mesh.tangents.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.tangents.constData()); | ||
| 2021 | networkMesh->_vertexBuffer->setSubData(colorsOffset, mesh.colors.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.colors.constData()); | ||
| 2022 | networkMesh->_vertexBuffer->setSubData(texCoordsOffset, | ||
| 2023 | mesh.texCoords.size() * sizeof(glm::vec2), (gpu::Byte*) mesh.texCoords.constData()); | ||
| 2024 | networkMesh->_vertexBuffer->setSubData(clusterIndicesOffset, | ||
| 2025 | mesh.clusterIndices.size() * sizeof(glm::vec4), (gpu::Byte*) mesh.clusterIndices.constData()); | ||
| 2026 | networkMesh->_vertexBuffer->setSubData(clusterWeightsOffset, | ||
| 2027 | mesh.clusterWeights.size() * sizeof(glm::vec4), (gpu::Byte*) mesh.clusterWeights.constData()); | ||
| 2028 | |||
| 2029 | networkMesh->_vertexStream = std::make_shared<gpu::BufferStream>(); | ||
| 2030 | if (mesh.tangents.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, 0, sizeof(glm::vec3)); | ||
| 2031 | if (mesh.colors.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, colorsOffset, sizeof(glm::vec3)); | ||
| 2032 | if (mesh.texCoords.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, texCoordsOffset, sizeof(glm::vec2)); | ||
| 2033 | if (mesh.clusterIndices.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, clusterIndicesOffset, sizeof(glm::vec4)); | ||
| 2034 | if (mesh.clusterWeights.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, clusterWeightsOffset, sizeof(glm::vec4)); | ||
| 2035 | |||
| 2036 | int channelNum = 0; | ||
| 2037 | networkMesh->_vertexFormat = std::make_shared<gpu::Stream::Format>(); | ||
| 2038 | networkMesh->_vertexFormat->setAttribute(gpu::Stream::POSITION, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); | ||
| 2039 | if (mesh.normals.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::NORMAL, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); | ||
| 2040 | if (mesh.tangents.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::TANGENT, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); | ||
| 2041 | if (mesh.colors.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::COLOR, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RGB)); | ||
| 2042 | if (mesh.texCoords.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::TEXCOORD, channelNum++, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); | ||
| 2043 | if (mesh.clusterIndices.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_INDEX, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW)); | ||
| 2044 | if (mesh.clusterWeights.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW)); | ||
| 2045 | } | ||
| 2046 | } | ||
| 2140 | 2047 | ||
| 2141 | { | 2048 | return networkMesh; |
| 2142 | networkMesh._vertexBuffer = std::make_shared<gpu::Buffer>(); | 2049 | } |
| 2143 | // if we don't need to do any blending, the positions/normals can be static | ||
| 2144 | if (mesh.blendshapes.isEmpty()) { | ||
| 2145 | int normalsOffset = mesh.vertices.size() * sizeof(glm::vec3); | ||
| 2146 | int tangentsOffset = normalsOffset + mesh.normals.size() * sizeof(glm::vec3); | ||
| 2147 | int colorsOffset = tangentsOffset + mesh.tangents.size() * sizeof(glm::vec3); | ||
| 2148 | int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3); | ||
| 2149 | int texCoords1Offset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2); | ||
| 2150 | int clusterIndicesOffset = texCoords1Offset + mesh.texCoords1.size() * sizeof(glm::vec2); | ||
| 2151 | int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4); | ||
| 2152 | |||
| 2153 | networkMesh._vertexBuffer->resize(clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4)); | ||
| 2154 | |||
| 2155 | networkMesh._vertexBuffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.vertices.constData()); | ||
| 2156 | networkMesh._vertexBuffer->setSubData(normalsOffset, mesh.normals.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.normals.constData()); | ||
| 2157 | networkMesh._vertexBuffer->setSubData(tangentsOffset, | ||
| 2158 | mesh.tangents.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.tangents.constData()); | ||
| 2159 | networkMesh._vertexBuffer->setSubData(colorsOffset, mesh.colors.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.colors.constData()); | ||
| 2160 | networkMesh._vertexBuffer->setSubData(texCoordsOffset, | ||
| 2161 | mesh.texCoords.size() * sizeof(glm::vec2), (gpu::Byte*) mesh.texCoords.constData()); | ||
| 2162 | networkMesh._vertexBuffer->setSubData(texCoords1Offset, | ||
| 2163 | mesh.texCoords1.size() * sizeof(glm::vec2), (gpu::Byte*) mesh.texCoords1.constData()); | ||
| 2164 | networkMesh._vertexBuffer->setSubData(clusterIndicesOffset, | ||
| 2165 | mesh.clusterIndices.size() * sizeof(glm::vec4), (gpu::Byte*) mesh.clusterIndices.constData()); | ||
| 2166 | networkMesh._vertexBuffer->setSubData(clusterWeightsOffset, | ||
| 2167 | mesh.clusterWeights.size() * sizeof(glm::vec4), (gpu::Byte*) mesh.clusterWeights.constData()); | ||
| 2168 | |||
| 2169 | // otherwise, at least the cluster indices/weights can be static | ||
| 2170 | networkMesh._vertexStream = std::make_shared<gpu::BufferStream>(); | ||
| 2171 | networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, 0, sizeof(glm::vec3)); | ||
| 2172 | if (mesh.normals.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, normalsOffset, sizeof(glm::vec3)); | ||
| 2173 | if (mesh.tangents.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, tangentsOffset, sizeof(glm::vec3)); | ||
| 2174 | if (mesh.colors.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, colorsOffset, sizeof(glm::vec3)); | ||
| 2175 | if (mesh.texCoords.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, texCoordsOffset, sizeof(glm::vec2)); | ||
| 2176 | if (mesh.texCoords1.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, texCoords1Offset, sizeof(glm::vec2)); | ||
| 2177 | if (mesh.clusterIndices.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, clusterIndicesOffset, sizeof(glm::vec4)); | ||
| 2178 | if (mesh.clusterWeights.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, clusterWeightsOffset, sizeof(glm::vec4)); | ||
| 2179 | |||
| 2180 | int channelNum = 0; | ||
| 2181 | networkMesh._vertexFormat = std::make_shared<gpu::Stream::Format>(); | ||
| 2182 | networkMesh._vertexFormat->setAttribute(gpu::Stream::POSITION, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); | ||
| 2183 | if (mesh.normals.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::NORMAL, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); | ||
| 2184 | if (mesh.tangents.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::TANGENT, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); | ||
| 2185 | if (mesh.colors.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::COLOR, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RGB)); | ||
| 2186 | if (mesh.texCoords.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::TEXCOORD, channelNum++, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); | ||
| 2187 | if (mesh.texCoords1.size()) { | ||
| 2188 | networkMesh._vertexFormat->setAttribute(gpu::Stream::TEXCOORD1, channelNum++, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); | ||
| 2189 | } else if (checkForTexcoordLightmap && mesh.texCoords.size()) { | ||
| 2190 | // need lightmap texcoord UV but doesn't have uv#1 so just reuse the same channel | ||
| 2191 | networkMesh._vertexFormat->setAttribute(gpu::Stream::TEXCOORD1, channelNum - 1, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); | ||
| 2192 | } | ||
| 2193 | if (mesh.clusterIndices.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_INDEX, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW)); | ||
| 2194 | if (mesh.clusterWeights.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW)); | ||
| 2195 | } | ||
| 2196 | else { | ||
| 2197 | int colorsOffset = mesh.tangents.size() * sizeof(glm::vec3); | ||
| 2198 | int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3); | ||
| 2199 | int clusterIndicesOffset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2); | ||
| 2200 | int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4); | ||
| 2201 | |||
| 2202 | networkMesh._vertexBuffer->resize(clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4)); | ||
| 2203 | networkMesh._vertexBuffer->setSubData(0, mesh.tangents.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.tangents.constData()); | ||
| 2204 | networkMesh._vertexBuffer->setSubData(colorsOffset, mesh.colors.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.colors.constData()); | ||
| 2205 | networkMesh._vertexBuffer->setSubData(texCoordsOffset, | ||
| 2206 | mesh.texCoords.size() * sizeof(glm::vec2), (gpu::Byte*) mesh.texCoords.constData()); | ||
| 2207 | networkMesh._vertexBuffer->setSubData(clusterIndicesOffset, | ||
| 2208 | mesh.clusterIndices.size() * sizeof(glm::vec4), (gpu::Byte*) mesh.clusterIndices.constData()); | ||
| 2209 | networkMesh._vertexBuffer->setSubData(clusterWeightsOffset, | ||
| 2210 | mesh.clusterWeights.size() * sizeof(glm::vec4), (gpu::Byte*) mesh.clusterWeights.constData()); | ||
| 2211 | |||
| 2212 | networkMesh._vertexStream = std::make_shared<gpu::BufferStream>(); | ||
| 2213 | if (mesh.tangents.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, 0, sizeof(glm::vec3)); | ||
| 2214 | if (mesh.colors.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, colorsOffset, sizeof(glm::vec3)); | ||
| 2215 | if (mesh.texCoords.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, texCoordsOffset, sizeof(glm::vec2)); | ||
| 2216 | if (mesh.clusterIndices.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, clusterIndicesOffset, sizeof(glm::vec4)); | ||
| 2217 | if (mesh.clusterWeights.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, clusterWeightsOffset, sizeof(glm::vec4)); | ||
| 2218 | |||
| 2219 | int channelNum = 0; | ||
| 2220 | networkMesh._vertexFormat = std::make_shared<gpu::Stream::Format>(); | ||
| 2221 | networkMesh._vertexFormat->setAttribute(gpu::Stream::POSITION, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); | ||
| 2222 | if (mesh.normals.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::NORMAL, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); | ||
| 2223 | if (mesh.tangents.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::TANGENT, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); | ||
| 2224 | if (mesh.colors.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::COLOR, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RGB)); | ||
| 2225 | if (mesh.texCoords.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::TEXCOORD, channelNum++, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); | ||
| 2226 | if (mesh.clusterIndices.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_INDEX, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW)); | ||
| 2227 | if (mesh.clusterWeights.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW)); | ||
| 2228 | 2050 | ||
| 2229 | } | 2051 | void NetworkGeometry::modelParseSuccess(FBXGeometry* geometry) { |
| 2230 | } | 2052 | // assume owner ship of geometry pointer |
| 2053 | _geometry.reset(geometry); | ||
| 2231 | 2054 | ||
| 2232 | _meshes.append(networkMesh); | 2055 | foreach(const FBXMesh& mesh, _geometry->meshes) { |
| 2056 | _meshes.emplace_back(buildNetworkMesh(mesh, _textureBaseUrl)); | ||
| 2233 | } | 2057 | } |
| 2234 | 2058 | ||
| 2235 | finishedLoading(true); | 2059 | _state = SuccessState; |
| 2060 | emit onSuccess(*this, *_geometry.get()); | ||
| 2061 | |||
| 2062 | delete _resource; | ||
| 2063 | _resource = nullptr; | ||
| 2064 | } | ||
| 2065 | |||
| 2066 | void NetworkGeometry::modelParseError(int error, QString str) { | ||
| 2067 | _state = ErrorState; | ||
| 2068 | emit onFailure(*this, (NetworkGeometry::Error)error); | ||
| 2069 | |||
| 2070 | delete _resource; | ||
| 2071 | _resource = nullptr; | ||
| 2236 | } | 2072 | } |
| 2237 | 2073 | ||
| 2238 | bool NetworkMeshPart::isTranslucent() const { | 2074 | bool NetworkMeshPart::isTranslucent() const { |
| 2239 | return diffuseTexture && diffuseTexture->isTranslucent(); | 2075 | return diffuseTexture && diffuseTexture->isTranslucent(); |
| 2240 | } | 2076 | } |
| 2241 | 2077 | ||
| 2242 | |||
| 2243 | bool NetworkMesh::isPartTranslucent(const FBXMesh& fbxMesh, int partIndex) const { | 2078 | bool NetworkMesh::isPartTranslucent(const FBXMesh& fbxMesh, int partIndex) const { |
| 2244 | assert(partIndex >= 0); | 2079 | assert(partIndex >= 0); |
| 2245 | assert(partIndex < parts.size()); | 2080 | assert((size_t)partIndex < _parts.size()); |
| 2246 | return (parts.at(partIndex).isTranslucent() || fbxMesh.parts.at(partIndex).opacity != 1.0f); | 2081 | return (_parts.at(partIndex)->isTranslucent() || fbxMesh.parts.at(partIndex).opacity != 1.0f); |
| 2247 | } | 2082 | } |
| 2248 | 2083 | ||
| 2249 | int NetworkMesh::getTranslucentPartCount(const FBXMesh& fbxMesh) const { | 2084 | int NetworkMesh::getTranslucentPartCount(const FBXMesh& fbxMesh) const { |
| 2250 | int count = 0; | 2085 | int count = 0; |
| 2251 | for (int i = 0; i < parts.size(); i++) { | 2086 | |
| 2087 | for (size_t i = 0; i < _parts.size(); i++) { | ||
| 2252 | if (isPartTranslucent(fbxMesh, i)) { | 2088 | if (isPartTranslucent(fbxMesh, i)) { |
| 2253 | count++; | 2089 | count++; |
| 2254 | } | 2090 | } |