* 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 | 14 | #include <cmath> |
| 15 | 15 | |
| 16 | 16 | #include <QNetworkReply> |
| 17 | −#include <QRunnable> | |
| 18 | 17 | #include <QThreadPool> |
| 19 | 18 | |
| 20 | 19 | #include <FSTReader.h> |
| @@ -50,6 +49,13 @@ GeometryCache::~GeometryCache() { · 29 unmodified lines | ||
| 50 | 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 | 59 | const int NUM_VERTICES_PER_TRIANGLE = 3; |
| 54 | 60 | const int NUM_TRIANGLES_PER_QUAD = 2; |
| 55 | 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 | 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 | 1652 | void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend) { |
| 1660 | 1653 | if (!_standardDrawPipeline) { |
| 1661 | 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; | |
| 1689 | − | |
| 1690 | −NetworkGeometry::NetworkGeometry(const QUrl& url, const QSharedPointer<NetworkGeometry>& fallback, bool delayLoad, | |
| 1691 | − const QVariantHash& mapping, const QUrl& textureBase) : | |
| 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); | |
| 1681 | +GeometryReader::GeometryReader(const QUrl& url, QNetworkReply* reply, const QVariantHash& mapping) : | |
| 1682 | + _url(url), | |
| 1683 | + _reply(reply), | |
| 1684 | + _mapping(mapping) { | |
| 1715 | 1685 | } |
| 1716 | 1686 | |
| 1717 | −bool NetworkGeometry::isLoadedWithTextures() const { | |
| 1718 | − if (!isLoaded()) { | |
| 1719 | − return false; | |
| 1720 | − } | |
| 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 | − } | |
| 1687 | +void GeometryReader::run() { | |
| 1688 | + try { | |
| 1689 | + if (!_reply) { | |
| 1690 | + throw QString("Reply is NULL ?!"); | |
| 1731 | 1691 | } |
| 1732 | − _isLoadedWithTextures = true; | |
| 1733 | − } | |
| 1734 | − return true; | |
| 1735 | −} | |
| 1692 | + QString urlname = _url.path().toLower(); | |
| 1693 | + bool urlValid = true; | |
| 1694 | + urlValid &= !urlname.isEmpty(); | |
| 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 { | |
| 1738 | − if (_lodParent.data() != this) { | |
| 1739 | − return _lodParent.data()->getLODOrFallback(distance, hysteresis, delayLoad); | |
| 1740 | − } | |
| 1741 | − if (_failedToLoad && _fallback) { | |
| 1742 | − return _fallback; | |
| 1743 | − } | |
| 1744 | − QSharedPointer<NetworkGeometry> lod = _lodParent; | |
| 1745 | − float lodDistance = 0.0f; | |
| 1746 | − QMap<float, QSharedPointer<NetworkGeometry> >::const_iterator it = _lods.upperBound(distance); | |
| 1747 | − if (it != _lods.constBegin()) { | |
| 1748 | − it = it - 1; | |
| 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(); | |
| 1698 | + if (urlValid) { | |
| 1699 | + // Let's read the binaries from the network | |
| 1700 | + FBXGeometry* fbxgeo = nullptr; | |
| 1701 | + if (_url.path().toLower().endsWith(".fbx")) { | |
| 1702 | + const bool grabLightmaps = true; | |
| 1703 | + const float lightmapLevel = 1.0f; | |
| 1704 | + fbxgeo = readFBX(_reply, _mapping, _url.path(), grabLightmaps, lightmapLevel); | |
| 1705 | + } else if (_url.path().toLower().endsWith(".obj")) { | |
| 1706 | + fbxgeo = OBJReader().readOBJ(_reply, _mapping, &_url); | |
| 1707 | + } else { | |
| 1708 | + QString errorStr("usupported format"); | |
| 1709 | + emit onError(NetworkGeometry::ModelParseError, errorStr); | |
| 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()) { | |
| 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; | |
| 1720 | + _reply->deleteLater(); | |
| 1788 | 1721 | } |
| 1789 | 1722 | |
| 1790 | −uint qHash(const QWeakPointer<Animation>& animation, uint seed = 0) { | |
| 1791 | − return qHash(animation.data(), seed); | |
| 1723 | +NetworkGeometry::NetworkGeometry(const QUrl& url, bool delayLoad, const QVariantHash& mapping, const QUrl& textureBaseUrl) : | |
| 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) { | |
| 1795 | − QVector<int> mappings = _jointMappings.value(animation); | |
| 1796 | − if (mappings.isEmpty() && isLoaded() && animation && animation->isLoaded()) { | |
| 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); | |
| 1735 | +NetworkGeometry::~NetworkGeometry() { | |
| 1736 | + if (_resource) { | |
| 1737 | + _resource->deleteLater(); | |
| 1802 | 1738 | } |
| 1803 | − return mappings; | |
| 1804 | 1739 | } |
| 1805 | 1740 | |
| 1806 | −void NetworkGeometry::setLoadPriority(const QPointer<QObject>& owner, float priority) { | |
| 1807 | − Resource::setLoadPriority(owner, priority); | |
| 1808 | − | |
| 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 | − } | |
| 1741 | +void NetworkGeometry::attemptRequest() { | |
| 1742 | + if (_state == DelayState) { | |
| 1743 | + attemptRequestInternal(); | |
| 1826 | 1744 | } |
| 1827 | 1745 | } |
| 1828 | 1746 | |
| 1829 | −void NetworkGeometry::setLoadPriorities(const QHash<QPointer<QObject>, float>& priorities) { | |
| 1830 | − Resource::setLoadPriorities(priorities); | |
| 1831 | − | |
| 1832 | − for (int i = 0; i < _meshes.size(); i++) { | |
| 1833 | − NetworkMesh& mesh = _meshes[i]; | |
| 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 | − } | |
| 1747 | +void NetworkGeometry::attemptRequestInternal() { | |
| 1748 | + if (_url.path().toLower().endsWith(".fst")) { | |
| 1749 | + requestMapping(_url); | |
| 1750 | + } else { | |
| 1751 | + requestModel(_url); | |
| 1849 | 1752 | } |
| 1850 | 1753 | } |
| 1851 | 1754 | |
| 1852 | −void NetworkGeometry::clearLoadPriority(const QPointer<QObject>& owner) { | |
| 1853 | − Resource::clearLoadPriority(owner); | |
| 1854 | − | |
| 1855 | − for (int i = 0; i < _meshes.size(); i++) { | |
| 1856 | − NetworkMesh& mesh = _meshes[i]; | |
| 1857 | − for (int j = 0; j < mesh.parts.size(); j++) { | |
| 1858 | − NetworkMeshPart& part = mesh.parts[j]; | |
| 1859 | − if (part.diffuseTexture) { | |
| 1860 | − part.diffuseTexture->clearLoadPriority(owner); | |
| 1861 | − } | |
| 1862 | − if (part.normalTexture) { | |
| 1863 | − part.normalTexture->clearLoadPriority(owner); | |
| 1864 | − } | |
| 1865 | − if (part.specularTexture) { | |
| 1866 | − part.specularTexture->clearLoadPriority(owner); | |
| 1867 | − } | |
| 1868 | − if (part.emissiveTexture) { | |
| 1869 | − part.emissiveTexture->clearLoadPriority(owner); | |
| 1755 | +bool NetworkGeometry::isLoaded() const { | |
| 1756 | + return _state == SuccessState; | |
| 1757 | +} | |
| 1758 | + | |
| 1759 | +bool NetworkGeometry::isLoadedWithTextures() const { | |
| 1760 | + if (!isLoaded()) { | |
| 1761 | + return false; | |
| 1762 | + } | |
| 1763 | + if (!_isLoadedWithTextures) { | |
| 1764 | + for (auto&& mesh : _meshes) { | |
| 1765 | + for (auto && part : mesh->_parts) { | |
| 1766 | + if ((part->diffuseTexture && !part->diffuseTexture->isLoaded()) || | |
| 1767 | + (part->normalTexture && !part->normalTexture->isLoaded()) || | |
| 1768 | + (part->specularTexture && !part->specularTexture->isLoaded()) || | |
| 1769 | + (part->emissiveTexture && !part->emissiveTexture->isLoaded())) { | |
| 1770 | + return false; | |
| 1771 | + } | |
| 1870 | 1772 | } |
| 1871 | 1773 | } |
| 1774 | + _isLoadedWithTextures = true; | |
| 1872 | 1775 | } |
| 1776 | + return true; | |
| 1873 | 1777 | } |
| 1874 | 1778 | |
| 1875 | 1779 | void NetworkGeometry::setTextureWithNameToURL(const QString& name, const QUrl& url) { |
| 1876 | 1780 | if (_meshes.size() > 0) { |
| 1877 | 1781 | auto textureCache = DependencyManager::get<TextureCache>(); |
| 1878 | − for (int i = 0; i < _meshes.size(); i++) { | |
| 1879 | − NetworkMesh& mesh = _meshes[i]; | |
| 1880 | − for (int j = 0; j < mesh.parts.size(); j++) { | |
| 1881 | − NetworkMeshPart& part = mesh.parts[j]; | |
| 1882 | − | |
| 1782 | + for (size_t i = 0; i < _meshes.size(); i++) { | |
| 1783 | + NetworkMesh& mesh = *(_meshes[i].get()); | |
| 1784 | + for (size_t j = 0; j < mesh._parts.size(); j++) { | |
| 1785 | + NetworkMeshPart& part = *(mesh._parts[j].get()); | |
| 1883 | 1786 | QSharedPointer<NetworkTexture> matchingTexture = QSharedPointer<NetworkTexture>(); |
| 1884 | 1787 | if (part.diffuseTextureName == name) { |
| 1885 | − part.diffuseTexture = textureCache->getTexture(url, DEFAULT_TEXTURE, _geometry.meshes[i].isEye); | |
| 1886 | − part.diffuseTexture->setLoadPriorities(_loadPriorities); | |
| 1788 | + part.diffuseTexture = textureCache->getTexture(url, DEFAULT_TEXTURE, _geometry->meshes[i].isEye); | |
| 1887 | 1789 | } else if (part.normalTextureName == name) { |
| 1888 | 1790 | part.normalTexture = textureCache->getTexture(url); |
| 1889 | − part.normalTexture->setLoadPriorities(_loadPriorities); | |
| 1890 | 1791 | } else if (part.specularTextureName == name) { |
| 1891 | 1792 | part.specularTexture = textureCache->getTexture(url); |
| 1892 | − part.specularTexture->setLoadPriorities(_loadPriorities); | |
| 1893 | 1793 | } else if (part.emissiveTextureName == name) { |
| 1894 | 1794 | part.emissiveTexture = textureCache->getTexture(url); |
| 1895 | − part.emissiveTexture->setLoadPriorities(_loadPriorities); | |
| 1896 | 1795 | } |
| 1897 | 1796 | } |
| 1898 | 1797 | } |
| 1899 | 1798 | } else { |
| 1900 | − qCDebug(renderutils) << "Adding a name url pair to pending" << name << url; | |
| 1901 | − // we don't have meshes downloaded yet, so hold this texture as pending | |
| 1902 | − _pendingTextureChanges.insert(name, url); | |
| 1799 | + qCWarning(renderutils) << "Ignoring setTextureWirthNameToURL() geometry not ready." << name << url; | |
| 1903 | 1800 | } |
| 1904 | 1801 | _isLoadedWithTextures = false; |
| 1905 | 1802 | } |
| 1906 | 1803 | |
| 1907 | 1804 | QStringList NetworkGeometry::getTextureNames() const { |
| 1908 | 1805 | QStringList result; |
| 1909 | − for (int i = 0; i < _meshes.size(); i++) { | |
| 1910 | − const NetworkMesh& mesh = _meshes[i]; | |
| 1911 | − for (int j = 0; j < mesh.parts.size(); j++) { | |
| 1912 | − const NetworkMeshPart& part = mesh.parts[j]; | |
| 1913 | − | |
| 1806 | + for (size_t i = 0; i < _meshes.size(); i++) { | |
| 1807 | + const NetworkMesh& mesh = *(_meshes[i].get()); | |
| 1808 | + for (size_t j = 0; j < mesh._parts.size(); j++) { | |
| 1809 | + const NetworkMeshPart& part = *(mesh._parts[j].get()); | |
| 1810 | + | |
| 1914 | 1811 | if (!part.diffuseTextureName.isEmpty() && part.diffuseTexture) { |
| 1915 | 1812 | QString textureURL = part.diffuseTexture->getURL().toString(); |
| 1916 | 1813 | result << part.diffuseTextureName + ":" + textureURL; |
| @@ -1935,320 +1832,259 @@ QStringList NetworkGeometry::getTextureNames() const { · 18 unmodified lines | ||
| 1935 | 1832 | return result; |
| 1936 | 1833 | } |
| 1937 | 1834 | |
| 1938 | −void NetworkGeometry::replaceTexturesWithPendingChanges() { | |
| 1939 | − QHash<QString, QUrl>::Iterator it = _pendingTextureChanges.begin(); | |
| 1940 | − | |
| 1941 | − while (it != _pendingTextureChanges.end()) { | |
| 1942 | − setTextureWithNameToURL(it.key(), it.value()); | |
| 1943 | − it = _pendingTextureChanges.erase(it); | |
| 1835 | +void NetworkGeometry::requestMapping(const QUrl& url) { | |
| 1836 | + _state = RequestMappingState; | |
| 1837 | + if (_resource) { | |
| 1838 | + _resource->deleteLater(); | |
| 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. | |
| 1948 | −class GeometryReader : public QRunnable { | |
| 1949 | −public: | |
| 1950 | − | |
| 1951 | − GeometryReader(const QWeakPointer<Resource>& geometry, const QUrl& url, | |
| 1952 | − QNetworkReply* reply, const QVariantHash& mapping); | |
| 1953 | − | |
| 1954 | − virtual void run(); | |
| 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) { | |
| 1845 | +void NetworkGeometry::requestModel(const QUrl& url) { | |
| 1846 | + _state = RequestModelState; | |
| 1847 | + if (_resource) { | |
| 1848 | + _resource->deleteLater(); | |
| 1849 | + } | |
| 1850 | + _resource = new Resource(url, false); | |
| 1851 | + connect(_resource, SIGNAL(loaded(QNetworkReply&)), SLOT(modelRequestDone(QNetworkReply&))); | |
| 1852 | + connect(_resource, SIGNAL(failed(QNetworkReply::NetworkError)), SLOT(modelRequestError(QNetworkReply::NetworkError))); | |
| 1970 | 1853 | } |
| 1971 | 1854 | |
| 1972 | −void GeometryReader::run() { | |
| 1973 | − QSharedPointer<Resource> geometry = _geometry.toStrongRef(); | |
| 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"); | |
| 1855 | +void NetworkGeometry::mappingRequestDone(QNetworkReply& reply) { | |
| 1856 | + assert(_state == RequestMappingState); | |
| 1989 | 1857 | |
| 1990 | − if (urlValid) { | |
| 1991 | − // Let's read the binaries from the network | |
| 1992 | − FBXGeometry fbxgeo; | |
| 1993 | − if (_url.path().toLower().endsWith(".fbx")) { | |
| 1994 | − bool grabLightmaps = true; | |
| 1995 | − float lightmapLevel = 1.0f; | |
| 1996 | − // HACK: For monday 12/01/2014 we need to kill lighmaps loading in starchamber... | |
| 1997 | − if (_url.path().toLower().endsWith("loungev4_11-18.fbx")) { | |
| 1998 | − grabLightmaps = false; | |
| 1999 | − } else if (_url.path().toLower().endsWith("apt8_reboot.fbx")) { | |
| 2000 | − lightmapLevel = 4.0f; | |
| 2001 | − } else if (_url.path().toLower().endsWith("palaceoforinthilian4.fbx")) { | |
| 2002 | − lightmapLevel = 3.5f; | |
| 2003 | − } | |
| 2004 | − fbxgeo = readFBX(_reply, _mapping, _url.path(), grabLightmaps, lightmapLevel); | |
| 2005 | − } else if (_url.path().toLower().endsWith(".obj")) { | |
| 2006 | − fbxgeo = OBJReader().readOBJ(_reply, _mapping, &_url); | |
| 1858 | + // parse the mapping file | |
| 1859 | + _mapping = FSTReader::readMapping(reply.readAll()); | |
| 1860 | + | |
| 1861 | + QUrl replyUrl = reply.url(); | |
| 1862 | + QString modelUrlStr = _mapping.value("filename").toString(); | |
| 1863 | + if (modelUrlStr.isNull()) { | |
| 1864 | + qCDebug(renderutils) << "Mapping file " << _url << "has no \"filename\" entry"; | |
| 1865 | + emit onFailure(*this, MissingFilenameInMapping); | |
| 1866 | + } else { | |
| 1867 | + // read _textureBase from mapping file, if present | |
| 1868 | + QString texdir = _mapping.value("texdir").toString(); | |
| 1869 | + if (!texdir.isNull()) { | |
| 1870 | + if (!texdir.endsWith('/')) { | |
| 1871 | + texdir += '/'; | |
| 2007 | 1872 | } |
| 2008 | − QMetaObject::invokeMethod(geometry.data(), "setGeometry", Q_ARG(const FBXGeometry&, fbxgeo)); | |
| 2009 | − } else { | |
| 2010 | − throw QString("url is invalid"); | |
| 1873 | + _textureBaseUrl = replyUrl.resolved(texdir); | |
| 2011 | 1874 | } |
| 2012 | 1875 | |
| 2013 | − } catch (const QString& error) { | |
| 2014 | − qCDebug(renderutils) << "Error reading " << _url << ": " << error; | |
| 2015 | − QMetaObject::invokeMethod(geometry.data(), "finishedLoading", Q_ARG(bool, false)); | |
| 1876 | + QUrl modelUrl = replyUrl.resolved(modelUrlStr); | |
| 1877 | + requestModel(modelUrl); | |
| 2016 | 1878 | } |
| 2017 | − _reply->deleteLater(); | |
| 2018 | 1879 | } |
| 2019 | 1880 | |
| 2020 | −void NetworkGeometry::init() { | |
| 2021 | − _mapping = QVariantHash(); | |
| 2022 | − _geometry = FBXGeometry(); | |
| 2023 | − _meshes.clear(); | |
| 2024 | − _lods.clear(); | |
| 2025 | − _pendingTextureChanges.clear(); | |
| 2026 | − _request.setUrl(_url); | |
| 2027 | − Resource::init(); | |
| 1881 | +void NetworkGeometry::mappingRequestError(QNetworkReply::NetworkError error) { | |
| 1882 | + assert(_state == RequestMappingState); | |
| 1883 | + _state = ErrorState; | |
| 1884 | + emit onFailure(*this, MappingRequestError); | |
| 2028 | 1885 | } |
| 2029 | 1886 | |
| 2030 | −void NetworkGeometry::downloadFinished(QNetworkReply* reply) { | |
| 2031 | − QUrl url = reply->url(); | |
| 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 | −} | |
| 1887 | +void NetworkGeometry::modelRequestDone(QNetworkReply& reply) { | |
| 1888 | + assert(_state == RequestModelState); | |
| 2071 | 1889 | |
| 2072 | −void NetworkGeometry::reinsert() { | |
| 2073 | − Resource::reinsert(); | |
| 2074 | − | |
| 2075 | − _lodParent = qWeakPointerCast<NetworkGeometry, Resource>(_self); | |
| 2076 | − foreach (const QSharedPointer<NetworkGeometry>& lod, _lods) { | |
| 2077 | − lod->setLODParent(_lodParent); | |
| 2078 | − } | |
| 1890 | + _state = ParsingModelState; | |
| 1891 | + | |
| 1892 | + // asynchronously parse the model file. | |
| 1893 | + GeometryReader* geometryReader = new GeometryReader(reply.url(), &reply, _mapping); | |
| 1894 | + connect(geometryReader, SIGNAL(onSuccess(FBXGeometry*)), SLOT(modelParseSuccess(FBXGeometry*))); | |
| 1895 | + connect(geometryReader, SIGNAL(onError(int, QString)), SLOT(modelParseError(int, QString))); | |
| 1896 | + | |
| 1897 | + QThreadPool::globalInstance()->start(geometryReader); | |
| 2079 | 1898 | } |
| 2080 | 1899 | |
| 2081 | −void NetworkGeometry::setGeometry(const FBXGeometry& geometry) { | |
| 2082 | − _geometry = geometry; | |
| 1900 | +void NetworkGeometry::modelRequestError(QNetworkReply::NetworkError error) { | |
| 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 | 1907 | auto textureCache = DependencyManager::get<TextureCache>(); |
| 2085 | − | |
| 2086 | − foreach (const FBXMesh& mesh, _geometry.meshes) { | |
| 2087 | − NetworkMesh networkMesh; | |
| 2088 | − | |
| 2089 | − int totalIndices = 0; | |
| 2090 | − bool checkForTexcoordLightmap = false; | |
| 2091 | − foreach (const FBXMeshPart& part, mesh.parts) { | |
| 2092 | − NetworkMeshPart networkPart; | |
| 2093 | − if (!part.diffuseTexture.filename.isEmpty()) { | |
| 2094 | − networkPart.diffuseTexture = textureCache->getTexture( | |
| 2095 | − _textureBase.resolved(QUrl(part.diffuseTexture.filename)), DEFAULT_TEXTURE, | |
| 2096 | − mesh.isEye, part.diffuseTexture.content); | |
| 2097 | − 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()); | |
| 1908 | + NetworkMesh* networkMesh = new NetworkMesh(); | |
| 1909 | + | |
| 1910 | + int totalIndices = 0; | |
| 1911 | + bool checkForTexcoordLightmap = false; | |
| 1912 | + | |
| 1913 | + // process network parts | |
| 1914 | + foreach (const FBXMeshPart& part, mesh.parts) { | |
| 1915 | + NetworkMeshPart* networkPart = new NetworkMeshPart(); | |
| 1916 | + | |
| 1917 | + if (!part.diffuseTexture.filename.isEmpty()) { | |
| 1918 | + networkPart->diffuseTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(part.diffuseTexture.filename)), DEFAULT_TEXTURE, | |
| 1919 | + mesh.isEye, part.diffuseTexture.content); | |
| 1920 | + networkPart->diffuseTextureName = part.diffuseTexture.name; | |
| 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 | − { | |
| 2128 | − networkMesh._indexBuffer = std::make_shared<gpu::Buffer>(); | |
| 2129 | − networkMesh._indexBuffer->resize(totalIndices * sizeof(int)); | |
| 2130 | − int offset = 0; | |
| 2131 | − foreach(const FBXMeshPart& part, mesh.parts) { | |
| 2132 | − networkMesh._indexBuffer->setSubData(offset, part.quadIndices.size() * sizeof(int), | |
| 2133 | − (gpu::Byte*) part.quadIndices.constData()); | |
| 2134 | − offset += part.quadIndices.size() * sizeof(int); | |
| 2135 | − networkMesh._indexBuffer->setSubData(offset, part.triangleIndices.size() * sizeof(int), | |
| 2136 | − (gpu::Byte*) part.triangleIndices.constData()); | |
| 2137 | − offset += part.triangleIndices.size() * sizeof(int); | |
| 1957 | + // initialize vertex buffer | |
| 1958 | + { | |
| 1959 | + networkMesh->_vertexBuffer = std::make_shared<gpu::Buffer>(); | |
| 1960 | + // if we don't need to do any blending, the positions/normals can be static | |
| 1961 | + if (mesh.blendshapes.isEmpty()) { | |
| 1962 | + int normalsOffset = mesh.vertices.size() * sizeof(glm::vec3); | |
| 1963 | + int tangentsOffset = normalsOffset + mesh.normals.size() * sizeof(glm::vec3); | |
| 1964 | + int colorsOffset = tangentsOffset + mesh.tangents.size() * sizeof(glm::vec3); | |
| 1965 | + int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3); | |
| 1966 | + int texCoords1Offset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2); | |
| 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 | − { | |
| 2142 | − networkMesh._vertexBuffer = std::make_shared<gpu::Buffer>(); | |
| 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)); | |
| 2048 | + return networkMesh; | |
| 2049 | +} | |
| 2228 | 2050 | |
| 2229 | − } | |
| 2230 | − } | |
| 2051 | +void NetworkGeometry::modelParseSuccess(FBXGeometry* geometry) { | |
| 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 | − | |
| 2235 | − finishedLoading(true); | |
| 2058 | + | |
| 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 | 2074 | bool NetworkMeshPart::isTranslucent() const { |
| 2239 | 2075 | return diffuseTexture && diffuseTexture->isTranslucent(); |
| 2240 | 2076 | } |
| 2241 | 2077 | |
| 2242 | − | |
| 2243 | 2078 | bool NetworkMesh::isPartTranslucent(const FBXMesh& fbxMesh, int partIndex) const { |
| 2244 | 2079 | assert(partIndex >= 0); |
| 2245 | − assert(partIndex < parts.size()); | |
| 2246 | − return (parts.at(partIndex).isTranslucent() || fbxMesh.parts.at(partIndex).opacity != 1.0f); | |
| 2080 | + assert((size_t)partIndex < _parts.size()); | |
| 2081 | + return (_parts.at(partIndex)->isTranslucent() || fbxMesh.parts.at(partIndex).opacity != 1.0f); | |
| 2247 | 2082 | } |
| 2248 | 2083 | |
| 2249 | 2084 | int NetworkMesh::getTranslucentPartCount(const FBXMesh& fbxMesh) const { |
| 2250 | 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 | 2088 | if (isPartTranslucent(fbxMesh, i)) { |
| 2253 | 2089 | count++; |
| 2254 | 2090 | } |